MimicGet the app

Make a game for the phone in your hand.

One script reads the Wand like a gamepad: aim, orientation, swing speed, buttons. A single HTML file is a game.

hello/index.html33 lines, the whole game
<!doctype html>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello, Wand</title>
<style>html,body{margin:0;height:100%;background:#0a0b0d;overflow:hidden}canvas{display:block}</style>
<canvas id="c"></canvas>
<script src="../lib/mimic.js"></script>
<script>
const mc = new MimicController();                 // the phone if one is paired, the mouse if not
const cv = document.getElementById('c'), ctx = cv.getContext('2d');
const spawn = () => ({ x: Math.random() * 1.4 - 0.7, y: Math.random() * 1.0 - 0.5 });
let target = spawn(), score = 0, flash = 0;
addEventListener('keydown', e => { if (e.key === 'r') mc.enableRumble(); });  // Chrome asks once

function frame() {
  mc.update();                                    // read the phone once per frame
  cv.width = innerWidth; cv.height = innerHeight;
  const c = v => Math.max(-1, Math.min(1, v));    // keep it on screen
  const px = p => [(c(p.x) + 1) / 2 * cv.width, (1 - c(p.y)) / 2 * cv.height];
  const [tx, ty] = px(target), [ax, ay] = px(mc.pointer);   // where the phone points
  if (Math.hypot(ax - tx, ay - ty) < 70 && mc.angularSpeed > 5) {  // a real swing, not a nudge
    score++; flash = 1; target = spawn();
    mc.rumble(0.8, 60);                           // the phone kicks (after R was pressed)
  }
  ctx.fillStyle = `rgba(255,180,84,${0.3 + flash})`; flash *= 0.9;
  ctx.beginPath(); ctx.arc(tx, ty, 54, 0, 7); ctx.fill();
  ctx.fillStyle = mc.isDown('a') ? '#ff6b4a' : '#5ee6d0';   // hold A (or click) to change colour
  ctx.beginPath(); ctx.arc(ax, ay, 12, 0, 7); ctx.fill();
  ctx.fillStyle = '#e8eaee'; ctx.font = '20px system-ui';
  ctx.fillText(`Swing through the orange circle. Score ${score}. Input: ${mc.source}`, 24, 40);
  requestAnimationFrame(frame);
}
frame();
</script>

What mimic.js gives you.

mc.pointer

Where it points, -1 to 1

mc.quat

Full 3D orientation

mc.angularSpeed

A nudge or a swing

mc.pressed(b)

Twelve buttons

mc.linearAccel

Thrusts, stabs, throws

mc.rumble()

Make the phone kick

mc.source

Mouse stands in without a phone

MimicController.all()

Up to four phones

Every value, in three.js axes: the full reference.

Make it play well.

1

Design for one button

In Grip only the volume keys reach. Never need more.

2

Menus by pointing

lib/wandcursor.js: point at a button, press A.

3

Keep the mouse in

People try it before they install anything.

4

Four phones, one screen

Each phone is its own controller.