> ## Documentation Index
> Fetch the complete documentation index at: https://docs.facebetter.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Prácticas recomendadas

> Rendimiento y seguridad del SDK Web Facebetter 2.0

<Note>
  SDK **2.0.0**. Autenticación: [Autenticación y licencia](/es/intro/license).
</Note>

## Autenticación en producción

Guarda `app_id` / `app_key` en el servidor. Obtén un `licenseToken` de corta duración desde **tu** backend y pásalo al motor.

```javascript theme={null}
const licenseToken = await fetch('/api/facebetter/auth', { method: 'POST' }).then((r) => r.text());
const engine = new BeautyEffectEngine(new EngineConfig({ licenseToken }));
```

<Warning>
  Nunca pongas `appKey` en un bundle de frontend.
</Warning>

Vincula el dominio exacto de producción en la Consola. Web no puede usar `.lic` sin conexión.

## Un motor por página

Crea **un** `BeautyEffectEngine`, llama a `init()` una vez y reutilízalo durante la sesión. No construyas un motor nuevo en cada fotograma.

```javascript theme={null}
useEffect(() => {
  let engine;
  (async () => {
    engine = new BeautyEffectEngine(new EngineConfig({ licenseToken }));
    await engine.init();
    engineRef.current = engine;
  })();
  return () => {
    engineRef.current?.destroy();
    engineRef.current = null;
  };
}, []);
```

## Bucle de fotogramas

* Impulsa el vídeo con `requestAnimationFrame`, no con un `while` ilimitado.
* Usa `FrameType.Video` para cámara; `FrameType.Image` para estáticos.
* Haz coincidir el tamaño del canvas con el `ImageData` procesado para evitar un escalado extra.
* Omite fotogramas hasta que `video.readyState >= 2` y `videoWidth > 0`.
* Prefiere `processImage` para cámara y canvas. `processTexture` no aplica espejo.

```javascript theme={null}
const result = engine.processImage(
  video,
  video.videoWidth,
  video.videoHeight,
  FrameType.Video,
  MirrorMode.Horizontal,
);
ctx.putImageData(result, 0, 0);
```

<h2 id="large-stills">
  Fotos grandes (vista previa y exportación)
</h2>

En galería o retoque, no envíes los píxeles a resolución completa a `processImage` en cada movimiento del deslizador. Al abrir el archivo, guarda dos búferes:

1. **Fotograma de vista previa** — un `ImageData` reducido (lado largo unos 1280–1440, o el tamaño CSS del canvas × `devicePixelRatio`, el que sea menor). Úsalo mientras el usuario ajusta efectos.
2. **Original** — el `HTMLImageElement` (o un bitmap sin reducir). Llama a `processImage` **una vez**, al exportar.

Usa `FrameType.Image` en ambos. Mismo motor, mismos setters. El tamaño de salida coincide con la entrada: la vista previa es pequeña; la exportación es a resolución completa. No amplíes el resultado de vista previa para fingir una exportación.

No pases un `<img>` sin reducir al bucle de vista previa: `processImage` lee `naturalWidth` / `naturalHeight`.

```javascript theme={null}
function rasterize(source, maxEdge) {
  const sw = source.naturalWidth || source.width;
  const sh = source.naturalHeight || source.height;
  const scale = Math.min(1, maxEdge / Math.max(sw, sh));
  const w = Math.max(1, Math.round(sw * scale));
  const h = Math.max(1, Math.round(sh * scale));
  const canvas = document.createElement('canvas');
  canvas.width = w;
  canvas.height = h;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(source, 0, 0, w, h);
  return ctx.getImageData(0, 0, w, h);
}

const preview = rasterize(image, 1440); // once, when the file opens
// Keep `image` for export

const previewOut = engine.processImage(
  preview,
  preview.width,
  preview.height,
  FrameType.Image,
);
previewCtx.putImageData(previewOut, 0, 0);

// Export — full resolution, once
const exportOut = engine.processImage(
  image,
  image.naturalWidth,
  image.naturalHeight,
  FrameType.Image,
);
```

La vista previa de cámara es otro camino: resolución de captura + `FrameType.Video`. Debounce las llamadas a `processImage` desde el deslizador. Muestra un estado de ocupado antes de exportar; el proceso a resolución completa es síncrono y puede congelar la página.

## Parámetros

Empieza bajo en vista previa en tiempo real (`smoothing` \~ `0.2`–`0.5`). El remodelado es `[-1, 1]`; los valores pequeños se ven más naturales en cámara.

No registres `onFaceLandmarks` salvo que la UI los necesite: fuerza detección por fotograma.

`setBeautySkinOnly(true)` evita manchar fondos cuando el suavizado es alto.

## Recursos

* Aloja `.fbd` de filtro / sticker y jpg/png de fondo como archivos estáticos; pasa la URL a `setFilter` / `setSticker` / `setVirtualBackground`.
* Los recursos de runtime los carga el SDK durante `init()`. No pongas un `resource.fbd` en la raíz del sitio para el motor.
* Cambia filtro / sticker con la entrada del usuario, no en cada fotograma.

## Memoria

* Llama a `destroy()` al desmontar / `beforeunload`.
* Reutiliza el canvas de visualización; no asignes un canvas nuevo por fotograma.

## HTTPS y CORS

El acceso a la cámara necesita HTTPS o localhost. Si los assets de filtro / sticker están en otro origin, envía cabeceras CORS.

## Relacionado

* [Manejo de errores](/es/web/error-handling)
* [Preguntas frecuentes](/es/web/faq)
* [Referencia de la API](/es/web/api-reference)
