> ## 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.

# ベストプラクティス

> Facebetter Web SDK 2.0 のパフォーマンスとセキュリティ

<Note>
  SDK **2.0.0**。認証: [認証とライセンス](/ja/intro/license)。
</Note>

## 本番の認証

`app_id` / `app_key` はサーバーに置きます。**自社バックエンド**が短命の `licenseToken` を取得し、エンジンへ渡します。

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

<Warning>
  `appKey` をフロントエンドバンドルに入れないでください。
</Warning>

コンソールに本番と同じドメインをバインドします。Web はオフライン `.lic` を使えません。

## 1 ページに 1 エンジン

`BeautyEffectEngine` を 1 つ作成し、`init()` を 1 回行い、セッション全体で再利用します。毎フレーム new しないでください。

```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;
  };
}, []);
```

## フレームループ

* 動画は `requestAnimationFrame` で駆動し、空の `while` は使わないでください。
* カメラは `FrameType.Video`、静止画は `FrameType.Image`。
* Canvas サイズを処理後の `ImageData` に合わせ、余分なスケールを避けます。
* `video.readyState >= 2` かつ `videoWidth > 0` になってから処理します。
* `processImage` を優先します。`processTexture` はミラーを適用しません。

```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">
  大きな静止画（プレビューと書き出し）
</h2>

ギャラリー写真やレタッチでは、スライダーを動かすたびに原寸ピクセルを `processImage` に渡さないでください。ファイルを開いたときにバッファを 2 つ用意します。

1. **プレビューフレーム** — 縮小した `ImageData`（長辺は約 1280–1440、または表示 canvas の CSS サイズ × `devicePixelRatio` の小さい方）。パラメータ調整中はこちらだけ処理します。
2. **原寸** — `HTMLImageElement`（または未縮小のビットマップ）。ユーザーが書き出すときに **1 回だけ** `processImage` します。

どちらも `FrameType.Image`。同じエンジン、同じ setter。出力サイズは入力と同じです。プレビュー結果を拡大して書き出しにしないでください。

縮小していない `<img>` をプレビューループに渡さないでください。`processImage` は `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,
);
```

カメラプレビューはこの経路ではありません。キャプチャ解像度 + `FrameType.Video` を使います。スライダーからの `processImage` は短く debounce してください。書き出しは同期でページが止まることがあるので、先に進行表示を出します。

## パラメータ

ライブプレビューは低強度から始めます（スムージングは約 `0.2`–`0.5`）。リシェイプ範囲は `[-1, 1]` で、カメラでは小さめの調整の方が自然です。

UI にキーポイントが不要なら `onFaceLandmarks` を登録しないでください。登録すると毎フレーム検出が走ります。

スムージングが高いときは `setBeautySkinOnly(true)` をオンにし、背景がぼやけないようにします。

## リソース

* フィルター / ステッカー `.fbd` と背景 jpg/png は静的ファイルとしてホストし、URL を `setFilter` / `setSticker` / `setVirtualBackground` に渡します。
* エンジンのランタイムリソースは SDK が `init()` 時に読み込みます。サイトルートに `resource.fbd` を置く必要はありません。
* フィルター / ステッカーの切り替えはユーザー操作時に行い、毎フレーム切り替えないでください。

## メモリ

* ページのアンロードやルート離脱時に `destroy()` を呼び出します。
* 表示用 canvas を再利用し、毎フレーム新規作成しないでください。

## HTTPS と CORS

カメラには HTTPS または localhost が必要です。フィルター / ステッカーなどの静的リソースが別オリジンにある場合は CORS を設定してください。

## 関連ドキュメント

* [エラー処理](/ja/web/error-handling)
* [よくある質問](/ja/web/faq)
* [API リファレンス](/ja/web/api-reference)
