MediaPipe Hands

página web “Partículas Interactivas con Manos”

El HTML usa MediaPipe Hands para detectar la mano en la webcam y, a partir de sus “landmarks”, calcula apertura (mano abierta/cerrada) y posición (x,y) de la mano para controlar el sistema de partículas hecho con Three.js. [pueden verse ejemplos parecidos en fritz​ y youtube]​

Librerías que intervienen

  • three (import map): se usa para toda la parte 3D (escena, cámara, partículas). ver fuente realacionada en developer.mozilla
  • @mediapipe/camera_utils: da la clase Camera que gestiona la captura de vídeo de la webcam y llama a onFrame. ver fuente realacionada en fritz
  • @mediapipe/hands: da la clase Hands, que ejecuta el modelo de IA de detección de manos y entrega multiHandLandmarks. ver fuente realacionada en youtube​ y fritz
  • @mediapipe/control_utils y drawing_utils podrían usarse para UI y dibujo, pero en este código solo se cargan; la lógica de gestos la implementas tú en onResults.ver fuente realacionada en fritz

Variables que representan el gesto

En la sección // --- 5. MEDIAPIPE HAND TRACKING --- se declaran las variables de estado que resumen el gesto:

  • handOpenness: valor en [0,1][0,1][0,1] que representa qué tan abierta está la mano (0 cerrada, 1 abierta).
  • handX, handY: posición de la mano normalizada al sistema de coordenadas de Three.js, de 1-1−1 a 111.
  • isHandDetected: indica si hay una mano detectada (para cambiar estado de la UI).

Estas variables son las que luego se usan en el bucle de animación para escalar, agitar y rotar las partículas.

Función onResults(results): interpretar landmarks

Esta función es el “callback” que MediaPipe Hands llama cada vez que procesa un frame:

  1. Comprobación de mano detectada
    • Si results.multiHandLandmarks tiene al menos una mano, se marca isHandDetected = true, se pone el punto de estado en verde y el texto “Mano detectada”.
    • Si no hay manos, se vuelve a “Buscando mano…” y se relaja la apertura hacia 1 (mano abierta por defecto).
  2. Lectura de landmarks
    • landmarks = results.multiHandLandmarks[0] toma la primera mano.
    • Usa índices estándar de MediaPipe:
      • landmarks[4]: punta del pulgar (thumbTip).
      • landmarks[8]: punta del índice (indexTip).
      • landmarks[0]: muñeca (wrist).fritz
  3. Cálculo de apertura de mano (handOpenness)
    • Calcula la distancia euclidiana en 2D entre pulgar e índice: jsconst distance = Math.sqrt( Math.pow(thumbTip.x - indexTip.x, 2) + Math.pow(thumbTip.y - indexTip.y, 2) );
    • Normaliza esa distancia a un rango 0–1: jslet openness = (distance - 0.05) * 5; openness = Math.max(0, Math.min(1, openness));
    • Suaviza la transición con un lerp para evitar jitter: jshandOpenness += (openness - handOpenness) * 0.1;
    Resultado: si pulgar e índice están cerca, distance es pequeña → mano “cerrada” (handOpenness bajo); si están separados, mano “abierta”.fritz
  4. Cálculo de posición de mano (handX, handY)
    • Toma la muñeca como punto de referencia de la mano: wrist.
    • Convierte coordenadas MediaPipe [0,1][0,1][0,1] a sistema Three.js [1,1][-1,1][−1,1] e invierte Y: jsconst targetX = (wrist.x - 0.5) * 2; // 0→-1, 1→1 const targetY = -(wrist.y - 0.5) * 2; // invierte eje vertical
    • Aplica también suavizado: jshandX += (targetX - handX) * 0.1; handY += (targetY - handY) * 0.1;
    Esto da una coordenada “suave” de la mano en el espacio de cámara que luego se usa para rotar la nube de partículas.

Objeto Hands y cámara MediaPipe

Debajo se instancia y configura el detector de manos:

  • const hands = new Hands({ locateFile: (file) => ... }): indica a MediaPipe dónde cargar los archivos del modelo desde el CDN.fritz
  • hands.setOptions({ maxNumHands: 1, modelComplexity: 1, ... }): limita a 1 mano y fija umbrales de detección y seguimiento.
  • hands.onResults(onResults): registra la función anterior como callback de resultados.

La cámara se configura con Camera de camera_utils:

jsconst cameraUtils = new Camera(videoElement, {
    onFrame: async () => {
        await hands.send({ image: videoElement });
    },
    width: 640,
    height: 480
});
cameraUtils.start();
  • Camera captura de la webcam al <video> oculto (#input-video).
  • En cada frame llama a onFrame, que a su vez envía la imagen actual a hands.send, disparando el pipeline de MediaPipe y finalmente onResults.fritz

Uso de handOpenness, handX, handY en las partículas

En el bucle animate() se conectan las variables de gesto con la animación:

  • baseScale = 0.2 + (handOpenness * 0.8):
    • Mano abierta (handOpenness≈1) → escala grande, la figura se expande.
    • Mano cerrada (handOpenness≈0) → escala pequeña, la figura se contrae.
  • noiseAmplitude = (1.0 - handOpenness) * 0.3:
    • Mano cerrada → ruido alto → partículas vibran, efecto “energía”.
    • Mano abierta → ruido bajo → partículas más estables.
  • Rotación según posición: jsparticles.rotation.y = handX * 1.5 + time * 0.1; particles.rotation.x = handY * 1.5; Mover la mano horizontal/verticalmente rota el conjunto de partículas, y se suma una pequeña rotación automática con el tiempo.
  • En el bucle por partícula, se hace “morphing” suave hacia targetPositions escaladas por baseScale, y se suma ruido aleatorio proporcional a noiseAmplitude cuando la mano está cerrada.

Con todo esto, el flujo queda así: webcam → CameraHands (MediaPipe) → onResults (landmarks → handOpenness, handX, handY) → animate() (aplica esos valores a escala, ruido y rotación de las partículas).youtubefritz

El siguiente paso podría ser añadir otra dimensión al gesto (por ejemplo, usar la altura de la mano o el número de dedos extendidos) para cambiar entre formas (corazón/flor/Saturno/fuegos) sin tocar el ratón.

  1. https://fritz.ai/introduction-to-hand-detection-in-the-browser-with-handtrack-js-and-tensorflow/
  2. https://www.youtube.com/watch?v=eI-d5yuPeJw
  3. https://developer.mozilla.org/es/docs/Learn_web_development/Core/Structuring_content/Basic_HTML_syntax
  4. VirtualWorlds.html

Aunque creo que la verdadera continuación de este capítulo podría estar en GitHub – Viral-Doshi/Gesture-Controlled-Virtual-Mouse: Virtually controlling computer using hand-gestures and voice commands. Using MediaPipe, OpenCV Python.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *