three-phone library illustration

Illustration by Angela Torchio

Start

Quick Start

Load three-phone as a plain script, load three.js with an import map, then a tiny loader makes THREE global and runs your sketch. Lock gestures and enable the capability your sketch needs from a user tap.

The three-phone + three.js head

<!-- three-phone: plain script, window globals -->
<script src="https://cdn.jsdelivr.net/npm/three-phone@0.1.0/dist/three-phone.min.js"></script>

<!-- three.js via import map -->
<script type="importmap">
{ "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.185.1/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.185.1/examples/jsm/" } }
</script>

<!-- loader: expose THREE, then run sketch.js (a plain script) -->
<script type="module">
  import * as THREE from 'three';
  window.THREE = THREE;
  const s = document.createElement('script');
  s.src = 'sketch.js';
  document.body.appendChild(s);
</script>
// sketch.js
let renderer, scene, camera, cube;

function init() {
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(innerWidth, innerHeight);
  document.body.appendChild(renderer.domElement);

  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
  camera.position.z = 5;
  scene.add(new THREE.HemisphereLight(0xffffff, 0x222233, 1));
  cube = new THREE.Mesh(new THREE.BoxGeometry(1.6, 2.6, 0.3),
    new THREE.MeshStandardMaterial({ color: 0x8b5cf6 }));
  scene.add(cube);

  showDebug();
  setPhoneCanvas(renderer);
  enableGyroTap('Tap to enable motion sensors');
  lockGestures();
  animate();
}

function animate() {
  requestAnimationFrame(animate);
  if (window.sensorsEnabled) applyDeviceRotation(cube, { smooth: 0.8 });
  else cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}

init();
Permission pattern: put code that uses phone hardware inside a positive enabled check — if (window.sensorsEnabled) { ... }, if (window.micEnabled) { ... }, or the matching status flag for the capability you enabled.

Motion Sensors

if (window.sensorsEnabled) {
  applyDeviceRotation(mesh, { smooth: 0.8 });
}

Touch Picking

function touchStarted() {
  const rc = getTouchRaycaster(mouseX, mouseY, camera);
  const hit = rc.intersectObjects(meshes)[0];
  if (hit) hit.object.material.color.set(0x8b5cf6);
}

Microphone

if (window.micEnabled) {
  const level = getMicLevel(); // 0..1
  blob.scale.setScalar(1 + level * 2);
}

Sound (three.js audio)

function userSetupComplete() {
  if (!window.soundEnabled) return;
  const sound = new THREE.Audio(listener);
  const osc = listener.context.createOscillator();
  osc.start(); sound.setNodeSource(osc);
}

Camera

cam = createPhoneCamera('user', true, 'cover');
cam.onReady(() => scene.add(cam.createBackgroundMesh()));
enableCameraTap();

Bluetooth (BLE)

bleSetup({
  characteristics: [
    { name: 'temp', type: 'float', notify: true }
  ]
});
enableBleTap();

function bleReceive(name, value) {
  debug(name + ' = ' + value);
}

Vibration

function touchStarted() {
  if (window.vibrationEnabled) vibrate(50);
}

Multiple Types

enablePermissionsTap(['sensors', 'mic'], 'Tap to enable motion + mic');

if (window.sensorsEnabled && window.micEnabled) {
  applyDeviceRotation(mesh, { smooth: 0.8 });
  mesh.scale.setScalar(1 + getMicLevel() * 2);
}
Script order matters: load three-phone.js first, then the import map, then the loader module. The loader sets window.THREE before it injects sketch.js, so your sketch can use the global THREE without being a module.
Tooling

Agent Skills

An optional skill teaches AI coding assistants how three-phone works, so generated sketches use the right permission flow, motion globals, and three.js helpers. See SKILL.md.

three-phone

Knowledge of the library API, the permission model, the motion/touch globals, and the three.js integration helpers (applyDeviceRotation, getTouchRaycaster, PhoneCamera).

p5-phone

Building the same ideas with p5.js instead of three.js? The p5-phone library shares this exact permission API.

Reference

API

Grouped by the job you are doing. The permission table keeps the five UI styles visible; each category lists the three-phone functions and the three.js APIs they pair with.

Examples

Example Catalog

Each example is a self-contained three.js sketch designed to show off 3D — lighting, materials, shadows, raycasting, spatial audio. Every card links to its source and, where one exists, its p5.js twin. Scan the QR to open on a phone.

Support

Compatibility

Plan mobile sketches by platform first. Browser permissions and hardware support matter more than the three.js version for most phone behavior.

iOS Safari

  • Motion sensors, microphone, sound, speech, and camera must start from a user tap or button.
  • Motion permission uses Apple-specific sensor prompts, so the enable helpers are important.
  • Web NFC, torch, and vibration are not available in Safari on iPhone.
  • Video textures need playsinline muted and a post-gesture play() — PhoneCamera handles this.

Android Chrome

  • Motion sensors usually work without the iOS-style sensor permission prompt.
  • Microphone, sound, speech, and camera still need a user gesture and browser permission.
  • Web NFC, torch, and vibration are Android Chrome features (NFC/BLE require HTTPS).
  • Web Bluetooth works in Chrome/Edge; on iOS use the Bluefy browser.
CapabilityiOS SafariAndroid ChromeDesktop browsers
three.js / WebGLSupported.Supported.Supported.
Motion sensorsSupported after a tap permission flow.Supported; permission request is usually a no-op.Limited or unavailable on most laptops and desktops.
Touch / pickingSupported through pointer events.Supported through pointer events.Use the mouse for testing (touches has one entry).
Microphone and soundSupported after user gesture and permission.Supported after user gesture and permission.Supported after browser permission.
Speech recognitionBrowser support varies; test on device.Supported where Web Speech is available.Browser support varies.
NFCNot supported.Supported in Chrome on HTTPS.Not supported.
GPS / geolocationSupported on HTTPS after a tap.Supported on HTTPS after a tap.Supported after browser permission.
Camera and ML5Supported after camera permission.Supported after camera permission.Supported after camera permission.
Magic-window ARSupported after camera + motion permission.Supported after camera + motion permission.Not supported (no motion sensors).
WebXR immersive-arNot supported.Supported in Chrome on HTTPS.Not supported.
VibrationNot supported.Supported on many phones.Limited or unavailable.
Torch / flashlightNot supported.Supported on many phones.Not supported.
Bluetooth (BLE)Bluefy browser only.Supported in Chrome on HTTPS.Chrome/Edge on HTTPS.