A digital life counter for Magic: The Gathering, Yu-Gi-Oh!, Pokémon TCG, and other tabletop games.
This is a fork of jontiritilli/life-puck with extended hardware support for multiple Waveshare ESP32-S3 display variants.
- Hardware Support
- New Features
- Software Features
- Getting Started
- Installation Guide
- Touch Calibration
- Adding Custom Presets
- Usage Guide
- Troubleshooting
- Contributing
- Credits
- License
This fork supports two Waveshare ESP32-S3 display variants:
| Board | Display | Touch | GPIO Expander | AliExpress Bundle |
|---|---|---|---|---|
| ESP32-S3-Touch-LCD-1.85C | ST77916 (QSPI) | CST816 | ✅ TCA9554 | ✅ Recommended (includes case + battery) |
| ESP32-S3-Touch-LCD-1.85 | ST77916 (QSPI) | CST816 | ❌ None | ❌ More expensive |
💡 Cost Tip: The 1.85C variant (at least in Europe) is significantly cheaper on AliExpress when purchased as a bundle with case and battery!
- MCU: ESP32-S3-WROOM-1 (N16R8) - 16MB Flash, 8MB PSRAM
- Display: 1.85" Round LCD, 360x360px, ST77916 driver (QSPI mode)
- Touch: Capacitive touch screen, CST816 controller
- Connectivity: USB-C, Wi-Fi, Bluetooth
- ✅ Dual board support: ESP32-S3-Touch-LCD-1.85C and 1.85
- ✅ GPIO Expander handling: Automatic detection and configuration for TCA9554
- ✅ Unified codebase: Single project for both hardware variants
- ✅ Arduino 3.x support: Uses latest ESP-IDF 5.3 based framework via pioarduino
- ✅ Multi-environment builds: Switch between boards with
platformio.inienvironments - ✅ Modern LVGL 9.3.0: Updated graphics library with performance improvements
- ✅ 🎲 Dice Roller: Roll D4, D6, D8, D10, D12, and D20
- ✅ 🪙 Coin Flip: Coin flip for random heads/tails decisions
- ✅ ⚙️ Game Presets: Pre-configured settings for popular TCGs (MTG, Yu-Gi-Oh!, Pokémon, FaB, Lorcana, One Piece)
- ✅ ✏️ Preset Editor: Create and save custom game configurations
- ✅ ⏱️ Round Timer: Track round duration with stopwatch and countdown modes
- ✅ 📊 Enhanced Timer: Switchable timer modes (stopwatch/countdown) with configurable round times
- ✅ Comprehensive documentation: Detailed setup and configuration guides
- ✅ Preset system: Pre-compile custom game presets to avoid on-device keyboard
- One and Two Player Modes: Track life totals for solo or head-to-head games
- Configurable Base Life: Set starting life from 1-999999
- Increment/Decrement Steps: Adjust life in small (tap) or large (swipe) steps
- Life Change History: Track all life changes
- Roll standard RPG/TCG dice (D4, D6, D8, D10, D12, D20)
- Clear result display
- Accessible from main menu
- 50/50 heads/tails outcomes
- Perfect for tiebreakers and random decisions
- Stopwatch Mode: Count up from 00:00 to track game duration
- Countdown Mode: Set a round time and count down with visual progress
- Persistent Timer: Continues running across screen changes
- Quick Controls: Tap to start/pause, long-press to reset
Pre-configured settings for popular games:
- MTG Standard - 20 life, ±1/±5 steps
- MTG Commander (EDH) - 40 life, ±1/±10 steps
- Pokémon TCG - 60 life, ±10/±30 steps
- Yu-Gi-Oh! - 8000 LP, ±50/±500 steps
- Flesh and Blood - 40 life, ±1/±5 steps
- Lorcana - 20 lore, ±1/±5 steps
- One Piece - 5 life, ±1/±2 steps
- Custom Slots 8-10 - Fully customizable
New Feature: 4 configurable auxiliary counters for tracking additional game metrics:
Counter Features:
- 4 Corner Positions: Top-Left, Top-Right, Bottom-Left, Bottom-Right
- Individual Enable/Disable: Each counter can be turned on/off independently
- Touch Interaction:
- Short Tap: Increment by 1
- Medium Hold: Decrement by 1
- Long Hold: Reset to 0
- Visual Feedback: Color changes during press (yellow for decrement, red for reset)
- Theme Integration: Counters adapt to selected theme colors
Configuration:
- Settings → Counters: Enable/disable individual counters
- Centralized Settings: All counter parameters configurable in
config.h - Positioning: Adjustable X/Y coordinates for each corner
- Visual Settings: Size, opacity, colors, and timing thresholds
- Reset Integration: Counters reset when main life counter is reset
Common Uses:
- MTG: Commander damage, poison counters, storm count, energy counters
- Pokémon: Energy counters, damage counters, special conditions
- Yu-Gi-Oh!: Life Point modifiers, special counters
- General: Any auxiliary tracking needed during gameplay
- Create custom game configurations
- Set starting life, step size, and theme
- Name your presets (up to 15 characters)
- Save to non-volatile storage
- Quick-select from game menu
Dedicated Audio submenu with:
- Audio Toggle: Enable/disable all sounds
- Volume Control: 0-100% (displayed as percentage, 9 internal steps)
- Sound Selection: Choose from 4 distinct alert sounds
- Visual feedback with grayed-out disabled states
Sound Options:
- Sound 1: Standard beep
- Sound 2: High-pitched tone
- Sound 3: Melodic chime
- Sound 4: Low buzz
Comprehensive power management in a dedicated submenu:
Brightness Control:
- Manual adjustment (0-100%)
Auto-Dim:
- Automatically reduces brightness to 25% after inactivity
- Default: 60 seconds
- Configurable timeout (0 = disabled)
- Restores full brightness on touch
Display Sleep:
- Turns off display completely after extended inactivity
- Default: 300 seconds (5 minutes)
- Configurable timeout (0 = disabled)
- Wakes on touch
Low Battery Dimming:
- Activates automatically at ≤15% battery
- Forces display to 5% brightness
- Prevents dimming when USB charging detected
- Automatically restores brightness when battery > 15%
- Works independently from auto-dim and sleep
Critical Battery Protection:
- Automatic deep sleep at ≤2% battery
- Smart USB detection:
- Voltage < 1V → USB charging detected, no shutdown
- 30-second timeout for switch toggling protection
- 10-second boot grace period for voltage stabilization
- Prevents battery damage from deep discharge
- Protects against false triggers during charging
- LVGL-based UI: Hardware-accelerated animations
- Intuitive Touch Controls: Tap, swipe, and long-press gestures
- In-app Configuration: Change all settings without recompiling
- Visual Feedback: Animated responses to all interactions
- Auto-brightness: Adjustable display brightness to save battery
- Sleep Mode: Auto-sleep after configurable inactivity period
- Robust State Persistence: All settings saved to NVS
- History Tracking: Persistent life change log until Reset
- Undo/Redo Support: Step back through life changes
- Session Recovery: Resume game state after restart
- when activated: All Lists an Settings menus can be closed by swiping from left to right
- Operating System: Windows, macOS, or Linux
- IDE: Visual Studio Code (recommended)
- Knowledge: Basic understanding of C++ and embedded systems
- Hardware: One of the supported Waveshare ESP32-S3 boards
- Visual Studio Code: Download
- PlatformIO Extension: Install from VS Code marketplace
- USB Drivers: CH340 (usually auto-installed)
- Download VS Code from https://code.visualstudio.com/
- Install and launch VS Code
- Open VS Code
- Go to Extensions (Ctrl+Shift+X)
- Search for "PlatformIO IDE"
- Click Install
- Restart VS Code when prompted
Option A: Using Git (recommended)
git clone https://github.com/Platinplayer23/Life-Puck---Multi-Hardware-Fork.git cd Life-Puck---Multi-Hardware-Fork-master
Option B: Download ZIP
- Click the green "Code" button on GitHub
- Select "Download ZIP"
- Extract the ZIP file
- Open VS Code
- File → Open Folder
- Select the
Life-Puck---Multi-Hardware-Fork-masterfolder - PlatformIO will automatically detect the project
Just switch between the Project Environments
- Connect your ESP32-S3 board via USB-C
- Press Ctrl+Alt+U (Cmd+Alt+U on Mac)
- Wait for compilation and upload to complete
Alternative: Using VS Code Upper Bar
- Click the → (Upload) icon in the upper bar
- Press Ctrl+Shift+P
- Type "PlatformIO: Serial Monitor"
- You should see debug output at 115200 baud
The device features an advanced 3-Round Calibration System that provides superior touch accuracy through multiple refinement passes.
Because of Problems with a standard 5 Point calibration, caused by an unknown transformation I implemented my own approach:
- ❌ No complex multi-point sampling
- ❌ No pixel-perfect alignment required
Instead:
- ✅ Fully automatic - System knows if you're left/right/up/down
- ✅ Zone-based detection - Touch target, system auto-corrects
- ✅ Real-time convergence - Parameters adjust while you touch
- ✅ 3-round refinement - Progressive accuracy improvement
- ✅ 13 total steps - Complete calibration with multiple passes
- ✅ Fallback system - Confirmation dialog with auto-revert
Round 1: Full Calibration (5 steps)
- Offset X - Touch red dot in center, system auto-adjusts horizontal offset
- Offset Y - Touch red dot in center, system auto-adjusts vertical offset
- Rotation - Touch red dot on vertical line, system auto-adjusts rotation
- Scale X - Touch red dot on right edge, system auto-adjusts horizontal scale
- Scale Y - Touch red dot on bottom edge, system auto-adjusts vertical scale
Round 2: Refinement (5 steps)
- Refine Offset X - Ultra-fine horizontal adjustment
- Refine Offset Y - Ultra-fine vertical adjustment
- Refine Rotation - Ultra-fine angle adjustment
- Refine Scale X - Ultra-fine horizontal scale
- Refine Scale Y - Ultra-fine vertical scale
Round 3: Final Offset (2 steps)
- Final Offset X - Ultimate horizontal precision
- Final Offset Y - Ultimate vertical precision
Summary Step
- Calibration complete! - Touch to exit and return to settings
-
Access Calibration:
- Go to Settings → Touch Calibration
- Read on-screen instructions
-
Follow the 3-Round Process:
- Round 1: Complete calibration (5 steps, each with Coarse → Fine)
- Round 2: Refinement of all parameters (5 ultra-fine steps)
- Round 3: Final offset adjustment (2 maximum precision steps)
- Just touch and hold the red dot or line as shown
- System adjusts parameters automatically
- Release when text says "OK! Release finger"
-
Confirmation:
- Calibration is immediately applied for testing
- On next boot, a confirmation dialog appears
- Choose Keep or Revert (10-second auto-revert if no action)
- Once confirmed, calibration is permanent
The 3-Round Calibration System uses progressive refinement to achieve optimal accuracy:
Round 1: Full Calibration (Coarse → Fine)
┌─────────────────────────────────────────┐
│ Step 1: Offset X (Coarse → Fine) │
│ Step 2: Offset Y (Coarse → Fine) │
│ Step 3: Rotation (Coarse → Fine) │
│ Step 4: Scale X (Coarse → Fine) │
│ Step 5: Scale Y (Coarse → Fine) │
└─────────────────────────────────────────┘
Round 2: Refinement (Ultra-Fine)
┌─────────────────────────────────────────┐
│ Step 6: Refine Offset X (Ultra-Fine) │
│ Step 7: Refine Offset Y (Ultra-Fine) │
│ Step 8: Refine Rotation (Ultra-Fine) │
│ Step 9: Refine Scale X (Ultra-Fine) │
│ Step 10: Refine Scale Y (Ultra-Fine) │
└─────────────────────────────────────────┘
Round 3: Final Offset (Maximum Precision)
┌─────────────────────────────────────────┐
│ Step 11: Final Offset X (Max Precision)│
│ Step 12: Final Offset Y (Max Precision)│
└─────────────────────────────────────────┘
The 3-round calibration process:
- Round 1: Establishes baseline calibration with coarse + fine passes
- Round 2: Refines all parameters with ultra-fine adjustments
- Round 3: Achieves maximum precision with final offset adjustments
- Progressive accuracy: Each round builds upon the previous for optimal results
- Origin Correction: A hardcoded origin correction is added to compensate standard origin offset
This creates a multi-pass refinement system that guarantees superior calibration accuracy!
Best method: Use the Preset Editor keyboard
- Go to Settings → Edit Presets
- Select any preset
- Try typing on the on-screen keyboard
- Test all screen regions (center, edges, corners)
- If touch feels off → Just re-run calibration (takes 45-65 seconds for full 3-round process!)
Why the keyboard?
- Small keys in all screen regions
- Immediate visual feedback
- Most challenging UI element - if keyboard works, everything works!
Calibration Timing:
- Round 1: 20-30 seconds (5 steps × 4-6 seconds each)
- Round 2: 15-25 seconds (5 refinement steps × 3-5 seconds each)
- Round 3: 6-10 seconds (2 final offset steps × 3-5 seconds each)
- Total: 45-65 seconds for complete 3-round calibration
If your device becomes unusable due to bad calibration, you can reset to factory defaults:
- Go to Settings → Touch Calibration
- The calibration will use factory defaults during the process
- Complete the 3-round calibration process (13 steps total)
If touch is completely broken:
- Open
src/main.cpparound line 125 - Uncomment the emergency reset line:
// === NOTFALL: Touch Calibration Reset === resetTouchCalibrationToDefaults(); // ← Remove the //
- Flash to device
- Re-comment the line and flash again
To change factory default values, edit src/hardware/display/lvgl_driver.cpp line 27-34:
static const float DEFAULT_CAL_MATRIX[7] = {
0.0f, // [0] offset_x
0.85f, // [1] scale_x - Try 0.80-1.05
0.0f, // [2] shear_xy - Usually 0.0
0.0f, // [3] offset_y
0.0f, // [4] shear_yx - Usually 0.0
1.0f, // [5] scale_y - Try 0.80-1.05
1.0f // [6] divisor - Leave at 1.0
};Tested Defaults: are saved in the Config.h
Recommendation: Use the automatic 3-round calibration instead of manual adjustment. It provides optimal, device-specific calibration in 45-65 seconds with superior accuracy through progressive refinement!
You can edit presets on-device via Settings → Edit Presets, but the keyboard can be challenging to use. For easier customization, edit presets before compilation.
Edit src/data/tcg_presets.cpp
Example: Customizing Slot 8
// ---- SLOT 8: Custom ----
strncpy(TCG_PRESETS[7].name, "Vanguard", sizeof(TCG_PRESETS[7].name) - 1);
TCG_PRESETS[7].starting_life = 5;
TCG_PRESETS[7].small_step = 1;
TCG_PRESETS[7].large_step = 2;Field Explanation:
name: Display name (max 15 characters)starting_life: Initial life total (1-9999)small_step: Small increment (tap)large_step: Large increment (swipe)
Cardfight!! Vanguard:
strncpy(TCG_PRESETS[7].name, "Vanguard", sizeof(TCG_PRESETS[7].name) - 1);
TCG_PRESETS[7].starting_life = 5;
TCG_PRESETS[7].small_step = 1;
TCG_PRESETS[7].large_step = 2;Digimon Card Game:
strncpy(TCG_PRESETS[8].name, "Digimon", sizeof(TCG_PRESETS[8].name) - 1);
TCG_PRESETS[8].starting_life = 5; // Security cards
TCG_PRESETS[8].small_step = 1;
TCG_PRESETS[8].large_step = 2;Star Wars Unlimited:
strncpy(TCG_PRESETS[9].name, "Star Wars", sizeof(TCG_PRESETS[9].name) - 1);
TCG_PRESETS[9].starting_life = 30;
TCG_PRESETS[9].small_step = 1;
TCG_PRESETS[9].large_step = 5;Life Adjustment:
- Tap Top Half: Increase life by small step
- Tap Bottom Half: Decrease life by small step
- Swipe Bottom to Top: Increase by large step
- Swipe Top to Bottom: Decrease by large step
Menu Access:
- Long press Center: Open contextual menu
- Swipe Right: Previous page in contextual menu
- Swipe Left: Next page in contextual menu
Timer:
- Tap Timer: Start/pause
- Long Press Timer: Reset
Simple Counters (if enabled):
- Tap Counter: Increment by 1
- Long Press Counter 0.5s: Decrement by 1
- Long Press Counter 1.5s: Reset Counter
Life Adjustment:
- Tap Your Half: Small adjustment
- Swipe Your Half: Large adjustment
Menu Access:
- Long press Center: Open contextual menu
Timer:
- Tap Timer: Start/pause
- Long Press Timer: Reset
- ⚙️ Settings Icon: Open settings menu
- 1P/2P Toggle: Switch between one and two player modes
- 🔄 Reset Icon: Reset all life counters to starting values
- 📋 Presets Icon: Quick-select game presets
- 🎲 Dice Icon (D): Open dice selection menu
- 🪙 Coin Icon (C): Instant coin flip
- History: Open the History
Main Settings:
- Start Life: Adjust starting life and step sizes for current session
- Power Settings → Power management submenu
- Audio Settings → Sound configuration submenu
- Timer Settings → Timer mode and round time submenu
- Counters → Simple counters configuration submenu
- Edit Presets: Customize game presets
- Touch Calibration: On-device touch calibration
- Swipe to Close: Enable/disable swipe gestures for menus
- Restart: Reboot device
- Battery Display: Real-time battery percentage and icon
Power Settings Submenu:
- Brightness: Adjust display brightness (0-100%)
- Auto-Dim: Auto-dim after inactivity (ON/OFF, configurable timeout)
- Sleep: Display sleep after inactivity (ON/OFF, configurable timeout)
- Low Battery Dimming: Automatic dimming at ≤15% battery (ON/OFF)
Audio Settings Submenu:
- Audio: Master audio toggle (ON/OFF)
- Volume: Adjust volume (0-100%)
- Sound: Select alert sound (4 options) used by Countdown Timer
Timer Settings Submenu:
- Timer: Show/hide timer on main screen (ON/OFF)
- Timer Mode: Stopwatch or Countdown
- Round Time: Set countdown duration (1-999 minutes, Countdown mode only)
Counters Settings Submenu:
- Top-Left Counter: Enable/disable counter in top-left corner
- Top-Right Counter: Enable/disable counter in top-right corner
- Bottom-Left Counter: Enable/disable counter in bottom-left corner
- Bottom-Right Counter: Enable/disable counter in bottom-right corner
- Open Contextual Menu: Long press center of screen
- Navigate to Page 2: Swipe left
- Select Dice Icon: Tap the "D" symbol
- Choose Dice Type: Select D4, D6, D8, D10, D12, D20, or D100
- View Result: Number displayed
- Close: Tap outside or swipe right (if enabled)
- Open Contextual Menu: Long press center
- Navigate to Page 2: Swipe left
- Select Coin Icon: Tap the "C" symbol
- View Result: Heads or Tails displayed
Stopwatch Mode:
- Settings → Timer Settings → Timer Mode → Stopwatch
- Timer counts up from 00:00
- Tracks total game duration
- No time limit
Countdown Mode:
- Settings → Timer Settings → Timer Mode → Countdown
- Settings → Timer Settings → Round Time → Enter duration (1-999 minutes)
- Timer counts down to 00:00
- Audio alert when time expires
- Automatic pause at 00:00
The Simple Counters system provides 4 configurable auxiliary counters for tracking additional game metrics:
Enable Counters:
- Settings → Counters → Enable desired counters (Top-Left, Top-Right, Bottom-Left, Bottom-Right)
- Counters appear in their respective corners
Usage:
- Short Tap: Increment by 1
- Medium Hold (0.5-1.5s): Decrement by 1 (visual feedback: yellow text)
- Long Hold (>1.5s): Reset to 0 (visual feedback: red text)
- Theme Integration: Counters adapt to selected theme colors
- Semi-Transparent: 80% opacity with gradient effects for better integration
Common Uses:
- MTG: Commander damage, poison counters, storm count, energy counters
- Pokémon: Energy counters, damage counters, special conditions
- Yu-Gi-Oh!: Life Point modifiers, special counters
- General: Any auxiliary tracking needed during gameplay
Configuration:
- All counter settings are centralized in
config.h - Adjustable positioning, size, opacity, colors, and timing
- Counters reset automatically when main life counter is reset
Using Preset Editor (On-Device):
- Settings → Edit Presets
- Select any preset (Custom 8, Custom 9, Custom 10 recommended)
- Enter preset name (max 15 characters, on-screen keyboard)
- Set starting life (1-9999)
- Set small step (tap increment)
- Set large step (swipe increment)
- Save preset (automatically saved to NVS)
Adding Presets Before Compilation (Recommended):
See Adding Custom Presets section for editing tcg_presets.cpp before building.
Extend Battery Life:
- Enable Auto-Dim (Settings → Power Settings → Auto-Dim → ON)
- Enable Sleep (Settings → Power Settings → Sleep → ON)
- Reduce brightness (Settings → Power Settings → Brightness → 30-50%)
Battery Protection:
- Low Battery Dimming activates automatically at ≤15%
- Wake device by pressing power button
- Critical Battery Protection triggers at ≤2%
- Device enters deep sleep to prevent battery damage
Charging:
- Connect USB-C cable
- Device can operate while charging
- Low Battery Dimming disabled during charging
- Battery percentage may show 0% while charging (normal behavior)
The Life Puck includes comprehensive power management to extend battery life and protect the battery from damage.
- Function: Reduces display to 25% brightness after inactivity
- Default: 60 seconds
- Configuration: Power Settings → Auto-Dim
- Benefits: Extends battery life during pauses in gameplay
- Function: Turns off display completely after extended inactivity
- Default: 300 seconds (5 minutes)
- Configuration: Power Settings → Sleep
- Wake: Touch screen anywhere
- Function: Forces display to 5% brightness at ≤15% battery
- Automatic: Activates when battery drops to critical levels
- Restoration: Automatically restores brightness when battery > 15%
- USB Detection: Disabled when charging (voltage < 1V)
- Configuration: Power Settings → Low Battery Dimming
Purpose: Prevents permanent battery damage from deep discharge
How It Works:
- Battery drops to ≤2%
- 2-second verification timer starts
- After 2 seconds, voltage is re-checked
- If voltage is between 1V and 3.3V → Deep sleep activated
- If voltage < 1V (USB charging) or > 3.3V → Protection canceled
Smart USB Detection:
- Voltage < 1V → USB charging detected, no shutdown
- 30-second timeout handles power switch toggling during USB charging
- 10-second boot grace period allows voltage readings to stabilize
Why This Matters:
- The original Life Puck had no automatic battery protection
- Without this feature, battery can drain to 2.7V, causing permanent damage
- This system ensures battery longevity and prevents costly replacements
Wake from Deep Sleep:
- connect USB-C cable
- Press and hold power button for 2 seconds (non C Model)
For Maximum Battery Life:
- Enable Auto-Dim and Sleep
- Set brightness to 30-50%
- Enable Low Battery Dimming
- Charge battery before it reaches 15%
For Maximum Performance:
- Set brightness to 70-100%
- Disable Auto-Dim
- Set Sleep timeout to 0 (disabled)
- Keep Low Battery Dimming enabled (battery protection)
Error: platform-espressif32 not found
Solution: Delete .pio folder and rebuild
Device Not Detected
- Install CH340 drivers: Download
- Try a different USB cable (must support data transfer)
- Hold BOOT button while connecting USB
Permission Denied (Linux)
sudo usermod -a -G dialout $USER Logout and login again
Display Shows Garbage
- Check board variant in
platformio.inimatches your hardware - Rebuild with
pio run -t clean && pio run
Colors Inverted
- Edit
src/Display_ST77916.cpp - Toggle
LCD_CMD_INVON/LCD_CMD_INVOFF
Touch Not Working
- Verify touch controller is CST816
- Check I2C address in
Touch_CST816.cpp(default: 0x15) - Adjust calibration (see Touch Calibration)
Device Crashes on Startup
Check serial monitor for error messages
pio device monitor -b 115200
Settings Not Saving
- NVS (Non-Volatile Storage) may be corrupted
- Erase flash:
pio run -t erase - Re-upload firmware:
pio run -t upload
Battery Drains Quickly
- Reduce display brightness in settings
- Check battery capacity (recommended: ≥ 500mAh)
- jontiritilli - Original Life Puck project: github.com/jontiritilli/life-puck
- Platinplayer23 - Multi-hardware support, dice roller, coin flip, enhanced timer, Arduino 3.x integration
- LVGL - Graphics library (v9.3.0)
- TFT_eSPI - Display driver
- ArduinoNvs - Non-volatile storage
- pioarduino - Arduino 3.x ESP32 platform
- Waveshare - ESP32-S3 display modules
- Espressif - ESP32-S3 MCU
This project inherits the license from the original jontiritilli/life-puck repository.
See LICENSE for details.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Original Project: jontiritilli/life-puck
Made with ❤️ for the tabletop gaming community