Developer guide

Best practices

Best Practices

Building a great motion game is mostly about respecting the player's body. They're standing in a living room, possibly tired, and they expect the game to feel responsive without being exhausting.

Gesture choice

✅ Prefer

  • Single, deliberate poses held briefly (raise an arm, clap, triangle)
  • Continuous-position controls for "analog" things (lean for steering, wrist Y for paddle position)
  • Big, low-precision motions — easy to perform, easy to detect

❌ Avoid

  • Tiny finger-precision controls — the platform tracks 21 hand landmarks but accuracy degrades quickly when the hand is far from the camera.
  • Sustained difficult poses — Flappy Arms originally required arms above shoulders and was exhausting in 30 seconds. We rewrote to "any rapid arm motion."
  • Conflicting gestures — clap and X-cross had overlapping wrist trajectories; we removed X-cross. Watch for similar collisions.
  • Gestures the user can't see — if the gesture happens behind the body or out of frame, no one knows what they're doing wrong. Show a skeleton HUD so the player sees themselves (every shipped Tezeract game does).

Input: pick the right level of abstraction

Game typeUse
Buttons + simple commands (jump, dodge, fire)InputListener (TezeractInput enum)
Named gestures (clap to play, wave to start)GestureListener (Gesture.NAME constants)
Continuous analog control (paddle, steering, wing flap velocity)MotionListener (raw MotionFrame keypoints)

Mix freely. Flappy Arms uses raw motion for the analog flap velocity; Shadow Boxing uses MotionListener + a custom PunchDetector; the launcher uses InputListener for gamepad-style nav.

Always ship a HUD

Every shipped Tezeract game shows a small (~150×220 dp) panel top-right with:

  1. A skeleton mirror of the player so they confirm "the camera sees me"
  2. A legend of the active controls — icons + labels

Without this, a stranger picking up your game can't figure out what to do. Copy BoxHUD from app-shadow-boxing or MotionHUD from app-launcher, restyle to match your game's palette.

Mirroring is selfie-style

The motion service mirrors X coordinates at the source — so when your game says "paddle at LEFT_WRIST.x", that paddle naturally follows the user's left hand on the left of the screen. You do not need to flip anything.

If you find yourself writing 1f - x, you're double-flipping.

Performance budgets

The motion service runs at ~30fps. Your game gets a frame callback for each. If you do > ~25ms of work in onMotionFrame, you start dropping frames.

WorkWhere
Update game stateOK in onMotionFrame
Heavy renderingPush to a separate game thread (see flappy / sample-game)
Async API calls / disk IOUse coroutines, don't block the binder thread

MotionListener.onMotionFrame runs on a binder thread. Marshal to the UI thread with Activity.runOnUiThread or mutableStateOf if you need to update Compose state from it.

First-run UX

The very first time a player opens your game they don't know:

  • Where to stand
  • Which gestures are mapped
  • That the camera is even working

Give them a 30-second onboarding with:

  • A target silhouette in the HUD viewport so they know where to position themselves
  • One on-screen prompt per control as you go ("now raise your right arm")
  • Skip on any input

app-flappy ships a faded T-pose silhouette in its HUD as the positioning guide. Steal the idea.

Accessibility

  • Don't require dual-arm motion if it's avoidable. A player with one arm can still raise an arm or lean. Where possible, make single-arm versions of multi-arm gestures (e.g. wave instead of arms-up).
  • Don't penalize standing still. Idle pose should never trigger an action. The ARMS_DOWN gesture is deliberately transition-based for this reason.
  • Audio cues, not just visual. Some players have low vision; a beep on a successful punch lands even if they can't see the +100 popup.

Power and thermals

The motion service runs continuously and the camera draws steady power. If your game is "open for hours" (a fitness routine, a party-mode rotation) consider:

  • Lower frame rate when nothing's happening on screen
  • A "rest" mode that pauses tracking after N seconds of no motion
  • Honest descriptions in the store ("intense — designed for 5–10 minute sessions")

What makes a game feel "Tezeract"

  • Clean motion-first UI. Buttons sized for 10-foot viewing, high contrast, big type.
  • Motion always has visible feedback. A flash, a particle, a HUD row that lights up. Players need to know their gesture registered.
  • Triangle to exit always works. Don't trap the player. The SDK handles this for you, but don't accidentally consume it.
  • Calibration-tolerant. Don't hard-code magic numbers like "shoulders at y=0.45". Pull from CalibrationProfile when you can, or compute baselines on first detected body.

We want the platform to feel coherent. Players should sense that any Tezeract game speaks the same body language as the launcher.