Piece Interaction Design - Board Docs

Piece Interaction Design

Board is a hybrid digital-physical platform. The Pieces are the controller: the way a player picks them up, slides them, rotates them, and sets them down is the input loop your game responds to. Good board games treat the physical motion as a first-class part of the design rather than as a translation layer on top of conventional UI.

This guide is mostly platform-general design guidance. It does not lean on any specific SDK, but every interaction described here maps to data already available from the input API. See Touch for the full code side: the contact model, phases, coordinate and orientation conventions, and how each SDK delivers contacts (Unity polls, Godot emits a signal, Web subscribes a callback). The snippets in this guide assume you have that loop running.

New to Board’s input model? Read Touch for how recognition works and Pieces for what a Glyph is and how a Piece Set maps to Glyph ids.


Why Pieces matter

A Piece is more than an avatar for a player. It is a physical object the player must touch, move, and reason about in three-space. That carries design consequences:

The best Board games are ones a player would still understand if you took the screen away: the Pieces alone tell you what is happening, and the screen is the amplifier.

In traditional video games, the controller is a foregone conclusion (pressing buttons on a gamepad, tapping keys, clicking a mouse, dragging on a touchscreen) and is neutral by design. The fun is on the screen, not in your hands. On Board the Pieces cannot be a neutral controller. If a player ever thinks “this would be easier with a mouse,” then the game’s mechanics are not native to Board. It has to be fun to play with Pieces.


Interaction types

There are seven core interaction primitives a player can perform with a Piece. Each maps cleanly to data your game reads from the contact stream: a contact’s lifecycle phase, its position, its orientation, and (for Pieces) whether a hand is touching it.

Place and Lift

The most fundamental interaction. A Piece arrives on the display (a contact in the Began phase) and eventually leaves (a contact in the Ended phase). Place-and-lift is the right primitive for:

When you process a contact, branch on its phase to spawn a visual on placement and tear it down on lift. (See Touch for the full snapshot loop that hands you these contacts each frame.)

Unity (C#)

using Board.Input;

void ProcessContact(BoardContact contact)
{
    switch (contact.phase)
    {
        case BoardContactPhase.Began:
            OnPiecePlaced(contact);   // arrived on the display
            break;
        case BoardContactPhase.Ended:
        case BoardContactPhase.Canceled:
            OnPieceLifted(contact);   // removed from the display
            break;
    }
}

Web (JS)

import { BoardContactPhase, type BoardContact } from "@board.fun/web-sdk";

function processContact(c: BoardContact) {
  switch (c.phase) {
    case BoardContactPhase.Began:
      onPiecePlaced(c);   // arrived on the display
      break;
    case BoardContactPhase.Ended:
    case BoardContactPhase.Canceled:
      onPieceLifted(c);   // removed from the display
      break;
  }
}

Godot (GDScript)

func process_contact(c: Dictionary) -> void:
    match c.phase_id:
        Board.input.PHASE_BEGAN:
            on_piece_placed(c)    # arrived on the display
        Board.input.PHASE_ENDED, Board.input.PHASE_CANCELED:
            on_piece_lifted(c)    # removed from the display

Design tips:

Slide

The Piece stays on the display while the player drags it. Sliding generates a stream of contacts in the Moved phase and is the primitive for:

Read the contact’s position on every Moved frame to follow the slide. Position is in display pixels in every SDK, but the origin, axis direction, and value type differ per engine (see the conventions table in Touch).

Unity (C#)

// Vector2 in Unity screen space (origin bottom-left, Y up).
if (contact.phase == BoardContactPhase.Moved)
{
    Vector2 pos = contact.screenPosition;
    DragUnitTo(pos);
}

Web (JS)

// Pixels, origin top-left, Y down (matches canvas / DOM coordinates).
if (c.phase === BoardContactPhase.Moved) {
  dragUnitTo(c.x, c.y);
}

Godot (GDScript)

# Pixels, origin top-left, Y down (matches Godot's 2D screen space).
if c.phase_id == Board.input.PHASE_MOVED:
    var pos := Vector2(c.x, c.y)
    drag_unit_to(pos)

Design tips:

Trace

A specialized slide where the shape of the path matters, not just the endpoint. Tracing a letter, a circle, or a constellation transforms Piece motion into gesture recognition. Mechanically it is the same Moved stream as Slide; the difference is that you accumulate the path and match it against a target shape.

Design tips:

Shake

Rapid back-and-forth motion of a Piece. Shake generates frequent Moved contacts with high velocity in alternating directions. Use it for:

Detect shake from velocity, not absolute position: compare the current position against the previous frame’s position for the same contact id and watch for rapid direction reversals. The conventions for which position field to read are in Touch.

Design tips:

Rotate

The player turns the Piece in place. The contact’s orientation changes while its position stays roughly constant. Use rotation for:

Orientation is reported only for Pieces (fingers have none). The units differ per engine: Unity reports radians, Godot and Web report degrees. Apply it to your visual in the engine’s native rotation convention.

Unity (C#)

// Radians, counter-clockwise from vertical.
float rotation = contact.orientation;
transform.rotation = Quaternion.Euler(0, 0, rotation * Mathf.Rad2Deg);

Web (JS)

// Degrees.
element.style.transform = `rotate(${c.orientation}deg)`;

Godot (GDScript)

# Degrees, screen-clockwise positive.
sprite.rotation_degrees = c.orientation

Design tips:

Twist

A quick, discrete rotational change, distinct from continuous rotation. Twisting a Piece could:

Twist is gesture, not value: do not read the final angle, read that the player turned the Piece quickly past a threshold. Compare orientation against the previous frame’s orientation for the same contact id and look for a large change in a short time.

Design tips:

Touch and Release (Hold)

The Piece stays on the display, but the player picks it up and holds it. Almost all of Board’s Pieces are made with a capacitive coating, so Board can detect whether a hand is on the Piece. That state is exposed as the contact’s touched flag, which flips true while the player holds the Piece. (Strata’s blocks are the exception: they are not capacitive, so they never report touched.) This is the primitive for:

There is no edge signal for touch and release: each SDK hands you a full per-frame snapshot of contacts (Unity polls it, Godot emits it on contacts_received, Web pushes it to your subscribe callback). To detect the touch and release edge, keep your own previous-frame map keyed by contact id and diff the touched flag each frame.

Unity (C#)

using System.Collections.Generic;
using Board.Input;

// contactId -> was the Piece touched last frame
readonly Dictionary<int, bool> _prevTouched = new();

void Update()
{
    var contacts = BoardInput.GetActiveContacts(BoardContactType.Glyph);
    foreach (var c in contacts)
    {
        bool wasTouched = _prevTouched.TryGetValue(c.contactId, out var t) && t;
        if (c.isTouched && !wasTouched)
            OnHoldBegan(c);       // picked up
        else if (wasTouched && !c.isTouched)
            OnHoldReleased(c);    // set down
    }

_prevTouched.Clear();
    foreach (var c in contacts)
        _prevTouched[c.contactId] = c.isTouched;
}

Web (JS)

import { type BoardContact } from "@board.fun/web-sdk";

// contactId -> was the Piece touched last frame
let prevTouched = new Map<number, boolean>();

function onContacts(contacts: ReadonlyArray<BoardContact>) {
  for (const c of contacts) {
    if (c.glyphId <= 0) continue;   // fingers have no hold state
    const wasTouched = prevTouched.get(c.contactId) ?? false;
    if (c.isTouched && !wasTouched) {
      onHoldBegan(c);       // picked up
    } else if (wasTouched && !c.isTouched) {
      onHoldReleased(c);    // set down
    }
  }
  prevTouched = new Map(contacts.map(c => [c.contactId, c.isTouched]));
}

Godot (GDScript)

var _prev := {}  # contact_id -> is_touched

func _on_contacts_received(contacts: Array) -> void:
    for c in contacts:
        if c.glyph_id <= 0:        # fingers, not Pieces, have no hold state
            continue
        var id: int = c.contact_id
        var was_touched: bool = _prev.get(id, false)
        if c.is_touched and not was_touched:
            on_hold_began(c)       # picked up
        elif was_touched and not c.is_touched:
            on_hold_released(c)    # set down
    _prev.clear()
    for c in contacts:
        _prev[c.contact_id] = c.is_touched

On Unity, fingers always report touched as true; on Godot and Web, fingers (Glyph id 0) carry no meaningful hold state. Either way, gate hold detection on Piece contacts so a finger never trips a hold. The snippets above do this by reading only Glyph contacts (Unity) or skipping Glyph id 0 (Godot, Web).

Design tips:


Piece categories

Pieces in a set typically fall into one of three functional categories. These are not enforced by the SDK; they are conventions that help players form mental models about what each Piece does. They are rough categories, not hard rules: some Pieces fit more than one, and some (like the blocks in Strata) fit none neatly.

Pawn Pieces (also called Character Pieces)

Pieces that represent the player or a player’s units, like classic board game pieces. They move around the board through placement and sliding, and they are persistent: the player keeps the same Pawn for the whole game. A Pawn does not have to be literally a character; it can be any object whose movement represents a player’s position or status.

Design conventions:

Examples: the robots in Cosmic Crush and Snek; the chopsticks in Omakase; Little Chef in Chop Chop.

Action Pieces (also called Verb Pieces)

Single-purpose Pieces that trigger discrete events when interacted with. The player picks up an Action Piece, uses it, and sets it back down, like cards in a hand. The physical design of an Action Piece advertises how it is meant to be used: a knife for slicing, a watering can for watering, a spaceship for shooting. When an Action Piece is used, the screen should visibly and audibly react.

Design conventions:

Examples: the knife in Chop Chop; a watering can; a spaceship in Space Rocks or Starfire.

Reaction Pieces (also called Platform Pieces)

Pieces placed on the board as persistent world elements. They do not move (or move rarely), and the digital game world responds to them being where they are. These Pieces can also create digital elements on screen, like the blocks that form a bungee in Save the Bloogs.

Design conventions:

Examples: the stairs and blocks in Save the Bloogs; terrain tiles in a strategy game; resource nodes in an economy game.

Note: Most Piece Sets mix all three categories. A well-designed set tells the player which category a Piece belongs to by its physical form: Pawns look like characters, Actions look like tools, Reactions look like terrain.

To branch behavior by which physical Piece is on the board, switch on the contact’s Glyph id (the index of that Piece within your Piece Set). Use the Glyph id, not the contact’s position or type, because the same physical Piece keeps its Glyph id across frames.

Unity (C#)

if (contact.type == BoardContactType.Glyph)
{
    switch (contact.glyphId)
    {
        case 0: HandlePawn(contact);     break;  // Pawn: place + slide
        case 1: HandleActionPiece(contact); break;  // Action: tap to commit
        case 2: HandleReactionPiece(contact); break;  // Reaction: persistent terrain
    }
}

Web (JS)

if (c.glyphId > 0) {
  switch (c.glyphId) {
    case 1: handlePawn(c);          break;  // Pawn: place + slide
    case 2: handleActionPiece(c);   break;  // Action: tap to commit
    case 3: handleReactionPiece(c); break;  // Reaction: persistent terrain
  }
}

Godot (GDScript)

# On the Godot channel use glyph_id (not type_id) to tell Pieces apart.
if c.glyph_id > 0:
    match c.glyph_id:
        1: handle_pawn(c)            # Pawn: place + slide
        2: handle_action_piece(c)    # Action: tap to commit
        3: handle_reaction_piece(c)  # Reaction: persistent terrain

Glyph ids are indices into your Piece Set. The exact value for each physical Piece is fixed by the Piece Set Model; to learn which id is which, place one Piece at a time and log the id. On Unity, finger contacts report a Glyph id of -1; on Godot and Web, fingers report 0. On Unity, Piece Glyph ids are 0-based (first Piece = 0) and fingers are -1, so isolate Pieces by checking type == BoardContactType.Glyph (not glyphId > 0). On Godot and Web, Piece Glyph ids are 1-based (first Piece = 1) and fingers are 0, so the glyphId > 0 guard isolates Pieces there. See Touch for the per-SDK details of reading Glyph ids and distinguishing fingers from Pieces.


Interfaces and signifiers

Part of the challenge of Board is that with a new kind of controller, you have to teach players how and where Pieces are used. Players need to be told when to place a Piece, what Piece to place, how to use it once placed, and when a placement is wrong. Board games use a handful of overlapping UI conventions for this.

Piece Indicators

On-screen cues that tell the player which Pieces are in play and where to place them. Chop Chop and Mushka use 3D renders of Pieces to show where to place them; Bloogs and Strata tutorials show which Piece to place and where.

Icons

Establish a consistent icon vocabulary so players learn each Piece’s meaning once. A sword Piece should be paired with the same sword glyph everywhere it appears: on the Piece indicator, on the action confirmation, on the score readout, on the tutorial overlay. Chop Chop shows an icon for which Piece to use at each station; Strata shows icons for the three Pieces in your turn; Omakase marks the current chopstick position with a chopstick icon.

Action Indicators

Visual cues for how to interact with a Piece: arrows showing direction of motion, rotational glyphs showing twist, particles showing shake. In Chop Chop a knife icon appears on the cutting board showing where and how to slice. Use these when the interaction is not obvious from the Piece’s shape.

Placement Confirmation

Feedback the moment a Piece arrives on a valid target, confirming both that the Piece was detected and that Board knows which Piece it is. This is the most important signifier you can ship, because players cannot recover from a placement they did not notice. Strata uses Piece confetti and outlines; Omakase shows petals; Chop Chop highlights stations; Bloogs shows a Piece-down confirmation.

Action Confirmation

Feedback that an Action Piece triggered, distinct from placement. The player needs to know “yes, I cast the spell,” not just “yes, I touched the Piece.” Chop Chop’s stations “pop” when an action completes.

Communicating Invalid Placement

Tell the player that the wrong Piece was placed, or placed in the wrong spot or at the wrong time. Silent rejection feels like a bug. Chop Chop highlights a tile red when the wrong Piece lands on it; Cosmic Crush shows an error state for an invalid robot position; Space Rocks indicates when a ship is off-sides; Bloogs pauses and shows a message.

Tutorialization

The first 30 seconds of a new player’s session is where you teach the mapping from physical Piece to game effect. Design that introduction explicitly rather than letting it emerge.


Design tips

Match interaction complexity to player skill

The first interaction a new player learns should be Place. Move them to Slide after they are comfortable. Reserve Twist, Shake, and Trace for players who have spent at least ten minutes with the game.

Avoid simultaneous required interactions

Asking a player to slide one Piece while twisting another asks them to use two hands at once. Stagger interactions: let the player commit one before the next is required.

Let the table read the state

The best Board moments happen when a player who is not looking at the screen can still tell what is going on. Lean on physical state: a Piece on a corner means something, a Piece in the middle means something else. Make those positions matter.

Account for table position

Players sit on all sides of the Board. Do not design Piece-orientation puzzles that only make sense from one seat. Use radial layouts, rotate UI per player when appropriate, and assume any Piece will be approached from any angle.

Design forgiving thresholds

Real-world physical interactions are noisy. A Piece may drift, players overshoot, hands tremble. Always design with thresholds that tolerate a few millimeters of slop and a few degrees of rotation error. The game should feel like it is working with the player, not testing their precision.

Use sound generously

Players spend much of their time looking at the Pieces, not the screen. Sound carries, so make sure every important state change has an audible counterpart.

Test on hardware

Touch interactions feel completely different on a real Board than in a desktop simulator. Get builds running on hardware as early as possible: the difference between “the player’s hand is heavier than the simulator’s mouse” and “the simulator does not capture inertia” is the difference between a game that ships and one that does not.


Putting it together

A well-designed Board game uses each interaction primitive for a single, distinct purpose, and tells the player which is which through visual and audio cues. A starting checklist:

  1. Identify the Pieces in your set. Sort them into Pawn, Action, and Reaction categories. Note which physical interactions each requires.
  2. Map each Piece to a primary interaction. Pawns use Place and Slide. Actions use Tap, Twist, or Shake. Reactions use Place.
  3. Plan the tutorialization. Apply the Tutorialization checklist to the player’s first 30 seconds: which Piece they touch first, what feedback they get, what they try next.
  4. Identify the failure modes. What happens when the player puts a Pawn on the wrong square? What happens when they twist instead of slide?
  5. Prototype on hardware. Build a five-minute slice and run it past someone who has never seen the game. Watch what they do with the Pieces: that tells you what your design is actually communicating.