Efecto finger frame: cómo funciona el marco de dedos
Junta pulgar e índice de las dos manos y sepáralos
Las dos “L” forman un marco. Dentro se ve lo mismo que estás grabando, pero con otro estilo.
El vídeo no sale de tu dispositivo: se procesa entero en tu navegador. Al pulsar se descargan unos 10 MB del modelo de detección de manos.
en reposo
El efecto se llama finger frame: haces un marco con los dedos de las dos manos y dentro aparece lo mismo que estás grabando, pero en dibujos animados, en negativo o en cómic. La demo de arriba es mi implementación, funciona con tu webcam y no manda nada a ningún servidor.
¿Cómo funciona?
El contenido de dentro del marco no está deformado, ni escalado, ni recortado. Es exactamente la misma imagen de la cámara, en la misma posición de pantalla, solo que con otro estilo aplicado. El cuadrilátero sí se inclina y se deforma siguiendo tus dedos, pero lo que se pinta dentro va alineado a pantalla.
Por eso funciona: la cara estilizada de dentro encaja píxel a píxel con tu cuerpo real de fuera. Es una ventana, no una pantalla.
Si deformases el vídeo en perspectiva para meterlo dentro del cuadrilátero, tendrías una tablet flotando entre las manos. Sería más difícil de programar y quedaría peor. La versión buena es la más simple, que no es lo habitual.
¿De dónde viene?
Aquí hay dos historias y se juntan en 2026.
El gesto viene del cine. Encuadrar con los dedos es lo que hacían los directores antes de que existiera el visor de director, ese pequeño telescopio que se cuelga del cuello y que en Europa se llegó a llamar por el nombre de un fabricante alemán, Tewe. Cuando llegó la herramienta, el gesto no desapareció: se le sigue llamando finger frame o director’s hands y se sigue usando en rodaje para ver un encuadre sin levantar la cámara.
El gesto ya significaba “esto es un encuadre” para cualquiera que lo viera, décadas antes de que existiera un ordenador capaz de detectarlo. En interfaces, dar con un gesto que la gente ya entiende sin que se lo expliques es media batalla ganada.
La tecnología viene de Google. MediaPipe se liberó en junio de 2019 en el CVPR, y el 19 de agosto de ese año publicaron el seguimiento de manos: 21 puntos por mano a partir de una sola cámara RGB, sin hardware especial. La arquitectura es la misma que corre en esta página, dos modelos en cascada: BlazePalm localiza la palma y devuelve una caja orientada, y un segundo modelo saca los 21 puntos dentro de ese recorte. Los números que daban entonces eran 16,1 ms en un Pixel 3 y 5,3 ms en un iPhone 11. Hasta ese momento esto pedía un escritorio potente o un sensor dedicado.
Y no lo construyeron para los filtros. En el anuncio hablaban de comprensión de lengua de signos, control por gestos y superponer contenido digital sobre el mundo físico.
Lo que cambió en 2026 no fue el seguimiento de manos, que lleva siete años resuelto, abierto y gratis. Fue lo de dentro del marco. Cuando aparecieron los modelos capaces de reestilizar vídeo en tiempo real, el mismo gesto de toda la vida pasó de ser un recorte con un filtro a ser una ventana a otro mundo. El efecto se hizo viral por la segunda mitad, no por la primera.
Las cinco piezas
- Captura: la webcam se pinta en espejo sobre un lienzo. Como está en espejo, hay que invertir también la coordenada x de los puntos de la mano, o el marco aparece en el lado contrario.
- Detección: MediaPipe Hand Landmarker corriendo en el navegador con WASM y GPU. Devuelve 21 puntos por mano.
- Gesto a cuadrilátero: de esos 21 puntos solo hacen falta cuatro por mano, y en realidad solo dos.
- Estabilización: aquí está casi todo el trabajo.
- Composición: se recorta al cuadrilátero y dentro se pinta el fotograma completo con el filtro.
De la mano al rectángulo
Las cuatro esquinas del marco son las puntas de índice y pulgar de cada mano. Para decidir si la mano está haciendo la “L” abierta, la regla es esta:
distancia(pulgar, índice) >= distancia(muñeca, nudillo_medio) * umbral
El detalle que importa es el denominador. La escala de la mano se mide de muñeca a nudillo medio, no sobre un dedo. Es la única medida que no se acorta cuando giras la mano o apuntas hacia la cámara. Si mides sobre los dedos, girar la muñeca apaga el efecto solo y no entiendes por qué.
El 80% del trabajo es estabilizar, no detectar
Un prototipo que detecta el gesto se hace en una tarde. Lo que separa eso de algo que puedes publicar son cuatro defensas, y ninguna es opcional:
| Mecanismo | El problema que resuelve |
|---|---|
| Histéresis del gesto | Cuesta abrir el marco y cuesta cerrarlo. Con un umbral único, girar los dedos lo apaga a media grabación. |
| Suavizado adaptativo a la velocidad | Poco gain con las manos quietas mata el temblor de píxel. Mucho gain en movimiento evita que el marco se despegue de los dedos. Un valor fijo te obliga a elegir cuál de los dos males prefieres. |
| Rechazo de teletransporte | Al cruzar los brazos el modelo confunde izquierda y derecha durante un fotograma y el marco pega un latigazo. Un salto enorme solo se acepta si se repite. |
| Sostener el marco al perder el tracking | Al cruzar las manos se tapan entre ellas y la detección se cae. Sin esto, el efecto parpadea justo en el momento más vistoso. |
Hay un detalle más, y es donde mi versión se separa de las que circulan. Las cuatro esquinas se pueden ordenar por dedo (índice de una mano, pulgar de la otra) o por ángulo alrededor del centro. Por dedo es más barato, pero la postura clásica de director, con una mano girada 180 grados, degenera en un lazo de dos triángulos. Yo ordeno por ángulo y después realineo contra el fotograma anterior probando las ocho combinaciones posibles de rotación y sentido. Sale siempre un cuadrilátero convexo, valga la postura que valga, y el suavizado sigue comparando cada esquina consigo misma.
Eso son cuarenta líneas de geometría pura, sin navegador ni cámara de por medio, así que están cubiertas por tests unitarios: umbrales del gesto, orden de esquinas, suavizado, rechazo de saltos y ventana de caída.
Las tres vías para lo de dentro
El árbol de decisión real es este:
| Vía | Motor | Latencia | Coste |
|---|---|---|---|
| Filtros locales | Shaders en tu propia GPU | Ninguna | Cero |
| IA en tiempo real | Modelo de vídeo a vídeo por WebRTC | Decenas de milisegundos | Por minuto, de pago |
| IA en diferido | Modelo de vídeo sobre un clip grabado | Minutos | Por vídeo |
Los vídeos que ves en redes con la persona convertida en anime o en LEGO dentro del marco usan la segunda. Es espectacular y no es gratis.
Esta demo usa la primera: diez filtros escritos en GLSL que corren en tu tarjeta gráfica. Dibujos animados (color aplanado a bandas más contorno de tinta por detección de bordes), negativo, cómic con trama de puntos girada, lápiz, pixelado, noir, glitch, VHS, neón y térmica.
Si montas la tercera vía, al prompt de estilo hay que concatenarle siempre una instrucción de alineación estricta, del tipo “mismo encuadre, sin zoom, sin recorte, ojos y boca en las mismas coordenadas de pantalla”. En cuanto el modelo recentra la cara, la ventana deja de encajar y el efecto se rompe.
¿Por qué en el navegador y no en Python?
En Python con OpenCV esto funciona igual de bien. Lo he considerado y lo he descartado por una razón: replicarlo.
En Python, compartirlo significa “instala Python, haz pip install mediapipe, ejecuta este script”, y aun así solo corre en tu máquina. En el navegador es un enlace. Webcam, GPU y cero instalación solo coinciden en un sitio, y es este.
Hay un precio y lo pagas al pulsar el botón: entre el runtime de WASM y el modelo de detección de manos son unos 10 MB, de los cuales 7,8 MB es el modelo. Por eso nada de eso se descarga al abrir esta página. Si no pulsas, este artículo pesa lo que cualquier otro. Es la misma decisión que tomarías en un producto: lo caro se paga cuando aporta valor, no por si acaso.
¿Cómo lo monto yo en mi web?
No hace falta instalar nada ni montar un build. MediaPipe se carga por CDN como un módulo ES, y el resto es Canvas 2D. Esto es un fichero HTML entero que funciona: guárdalo, sírvelo y ya tienes el efecto.
El HTML es esto y nada más:
<video id="cam" playsinline muted></video>
<canvas id="lienzo"></canvas>
<script type="module" src="./marco.js"></script>
Y marco.js:
import { HandLandmarker, FilesetResolver }
from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.14";
const CDN = "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.14";
const MODELO = "https://storage.googleapis.com/mediapipe-models/hand_landmarker" +
"/hand_landmarker/float16/1/hand_landmarker.task";
const video = document.getElementById("cam");
const lienzo = document.getElementById("lienzo");
const ctx = lienzo.getContext("2d");
video.style.display = "none";
video.srcObject = await navigator.mediaDevices.getUserMedia({
video: { width: 1280, height: 720, facingMode: "user" },
});
await video.play();
lienzo.width = video.videoWidth;
lienzo.height = video.videoHeight;
const landmarker = await HandLandmarker.createFromOptions(
await FilesetResolver.forVisionTasks(`${CDN}/wasm`),
{
baseOptions: { modelAssetPath: MODELO, delegate: "GPU" },
runningMode: "VIDEO",
numHands: 2,
}
);
// De los 21 puntos por mano solo hacen falta estos cuatro.
const MUNECA = 0, PULGAR = 4, INDICE = 8, NUDILLO = 9;
// El vídeo se pinta en espejo, así que la x del landmark también se invierte.
const aPixel = (p) => ({ x: (1 - p.x) * lienzo.width, y: p.y * lienzo.height });
const dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
function pintarVideo() {
ctx.save();
ctx.translate(lienzo.width, 0);
ctx.scale(-1, 1);
ctx.drawImage(video, 0, 0, lienzo.width, lienzo.height);
ctx.restore();
}
function marcoDe(manos) {
if (manos.length !== 2) return null;
const puntas = [];
for (const mano of manos) {
// Escala de muñeca a nudillo: no se acorta cuando la mano gira.
const escala = dist(aPixel(mano[MUNECA]), aPixel(mano[NUDILLO]));
const pulgar = aPixel(mano[PULGAR]);
const indice = aPixel(mano[INDICE]);
if (dist(pulgar, indice) < escala * 0.75) return null; // la "L" no está abierta
puntas.push(indice, pulgar);
}
// Orden angular alrededor del centro: sale convexo con cualquier postura.
const cx = puntas.reduce((s, p) => s + p.x, 0) / 4;
const cy = puntas.reduce((s, p) => s + p.y, 0) / 4;
return puntas.sort((a, b) =>
Math.atan2(a.y - cy, a.x - cx) - Math.atan2(b.y - cy, b.x - cx));
}
let ultimoFrame = -1;
let manos = [];
function bucle() {
pintarVideo();
if (video.currentTime !== ultimoFrame) {
ultimoFrame = video.currentTime;
manos = landmarker.detectForVideo(video, performance.now()).landmarks ?? [];
}
const marco = marcoDe(manos);
if (marco) {
ctx.save();
ctx.beginPath();
ctx.moveTo(marco[0].x, marco[0].y);
for (const p of marco.slice(1)) ctx.lineTo(p.x, p.y);
ctx.closePath();
ctx.clip();
ctx.filter = "invert(1)"; // aquí va el filtro que quieras
pintarVideo(); // el fotograma ENTERO, recortado al marco
ctx.restore();
}
requestAnimationFrame(bucle);
}
bucle();
Esas dos últimas líneas dentro del if son el efecto entero: se recorta al cuadrilátero y se pinta el fotograma completo encima. No se deforma nada. Por eso lo de dentro encaja al píxel con lo de fuera.
Tres cosas que te van a morder
getUserMediasolo funciona enlocalhosto en HTTPS. Abrir el fichero con doble clic no vale: necesitas un servidor, aunque seapython -m http.server.- El modo VIDEO exige marcas de tiempo crecientes. Si le pasas el mismo fotograma dos veces, MediaPipe lanza un error. De ahí la comparación con
video.currentTime, que es la forma barata de saber si el vídeo ha avanzado. - El modelo son 7,8 MB y el runtime de WASM otros dos y pico. Si lo cargas al abrir la página, se lo comes en tu métrica de carga. Ponlo detrás de un botón, como está en la demo de arriba.
Qué le falta a este ejemplo
Funciona, pero es el prototipo de la tarde, no lo que puedes publicar. Le faltan justamente las cuatro defensas de las que hablaba antes: sin histéresis el marco se apaga al girar los dedos, sin suavizado tiembla con las manos quietas, sin rechazo de saltos pega latigazos al cruzar los brazos y sin ventana de caída parpadea cuando una mano tapa a la otra. Son unas cien líneas más, y son las que separan esto de la demo de arriba.
Para los filtros, ctx.filter te da gratis invert, grayscale, blur, sepia, contrast y hue-rotate. Para dibujos animados, cómic o neón necesitas leer píxeles, y ahí conviene pasarse a WebGL con un shader, que es lo que usa la demo de esta página.
¿Para qué sirve aparte de para un vídeo?
Lo interesante no es el efecto. Es que el seguimiento de manos en tiempo real con una webcam corriente hoy es gratis, corre en el dispositivo y no necesita nada más. Y eso vale dinero justo donde no puedes o no quieres tocar una pantalla.
Lo que ya funciona hoy
- Quirófanos. Es el caso mejor documentado de todos. Consultar un TAC o una resonancia a mitad de intervención obliga a tocar un teclado o un ratón que no están estériles. Hay una línea entera de investigación sobre navegar imágenes médicas por gestos precisamente para no romper el campo estéril, y los sistemas más recientes lo resuelven con una sola cámara RGB y MediaPipe, sin añadir hardware al quirófano.
- Coches. BMW lleva control por gestos en producción desde 2015, con un sensor 3D en el techo junto al retrovisor: subir el volumen, aceptar o rechazar una llamada. La idea de fondo es la misma, no apartar la vista de la carretera ni buscar un botón con el dedo.
- Lengua de signos. Era la primera aplicación que citaba Google en 2019 y sigue siendo el terreno donde más se investiga.
Dónde lo usaría yo
El marco no tiene por qué ser un efecto. Puede ser un selector. Enmarcas una zona de lo que ve la cámara y sobre eso, y solo sobre eso, se lanza algo: leer el texto que hay dentro, traducirlo, buscar ese producto, medir esa pieza, contar cuántas unidades hay. Es una forma física de decirle a un sistema “de todo lo que estás viendo, me refiero a esto”, que es justo el problema que tiene hoy cualquier asistente con cámara.
A partir de ahí se abren solas unas cuantas:
- Taller, laboratorio y obrador, donde se trabaja con guantes o con las manos sucias y tocar una pantalla es un incordio o directamente una contaminación.
- Soporte remoto y formación: el técnico enmarca la pieza y el experto que está al otro lado ve exactamente de qué le está hablando, sin tener que describirla.
- Museos y retail: enmarcas una obra o un producto y aparece su ficha, sin apps que instalar ni códigos QR que buscar.
- Accesibilidad: un gesto amplio con las dos manos es viable para quien no maneja bien un ratón o una pantalla táctil pequeña.
¿Cuándo podría usarlo en producción?
Cuando el gesto deje de dispararse solo. En una interfaz de verdad el enemigo no es el falso negativo, es el falso positivo: que el sistema crea que has hecho el gesto cuando solo estabas gesticulando al hablar. Para eso están exactamente la histéresis y la puerta de área mínima que conté antes. Un gesto de control tiene que ser deliberado, difícil de hacer sin querer, y a la vez tiene que perdonarte que lo mantengas regular. Ahí es donde se va el tiempo, no en el modelo.
Fuentes
- On-Device, Real-Time Hand Tracking with MediaPipe, Google Research, 19 de agosto de 2019. El anuncio original, con la arquitectura en cascada y los tiempos por dispositivo.
- MediaPipe Hands: On-device Real-time Hand Tracking, Zhang et al., 2020. El artículo con el detalle del modelo.
- Hand Landmarker, documentación actual de la tarea que usa esta demo.
- Hand Gesture Recognition for Contactless Device Control in Operating Rooms, sobre el problema de la esterilidad en quirófano.
Lo que me llevo
Tres cosas, y las tres se parecen sospechosamente a lo que pasa en proyectos que no tienen nada que ver con webcams:
- La versión buena era la más simple. Recortar y pintar alineado a pantalla es menos código que deformar en perspectiva, y además queda mejor.
- El modelo era la parte fácil. MediaPipe funciona a la primera. Lo que costó fue todo lo que hay alrededor: los umbrales, el suavizado, qué hacer cuando la detección se cae. Es exactamente lo que separa una demo de algo en producción.
- La restricción define la arquitectura. “Que cualquiera lo pueda probar sin instalar nada” decidió el lenguaje, el motor de render y hasta cómo se carga el modelo.
Si tienes un proceso en tu empresa donde la parte de IA es la fácil y lo difícil es todo lo demás, cuéntamelo y te digo por dónde lo abordaría.