▍AI Dev Lab
BGSnap

BGSnap ができるまで — ブラウザ内ONNXで画像の背景を切り抜く設計

背景透過ツールの開発ログ。 @imgly/background-removal をブラウザ内で動かして 画像から人物や物だけを切り抜く仕組みと、 待ち時間の見せ方をどう設計したかを記録しています。

·decision改善·stage公開中

BGSnap は、画像の背景を 一切サーバーに送らずに 透過するWebサービスです。 ONNX Runtime Web + WebAssembly でブラウザに被写体抽出モデルを呼び込み、 アップロードを完全に省きました。voice-scribe (音声) と clip-cast (動画) に続く、メディア処理ラボの3本目の柱です。

#なぜこの形にしたか

「背景を透過するために、画像をクラウドにアップロードする」 のは技術的にはともかく、心理的にとても引っかかります。 プロフィール写真、商品写真、子供の写真、本人確認書類の切り抜き — これらを得体の知れないSaaSにアップしている人がどれだけ自覚的に同意しているか怪しいです。

WebAssembly + ONNX Runtime Web の組み合わせがブラウザでまともに動くようになって、 「ML推論をブラウザ内で完結させる」 構成が現実的になりました。 @imgly/background-removal はそれを「3行で呼べる」ところまで整えたOSSライブラリです。

このサービスは、その構成を そのまま製品の体感に翻訳した ことが本体です。何も特別なアルゴリズムは無く、ただ「アップロードしない」 を一発で伝える形に落とし込みました。

#見た目の方針

フォトスタジオ / ライトボックス。

  • 深いプラム (#0c0716) を背景に、エレクトリックバイオレット (#a259ff) を主アクセント
  • ホットマゼンタ (#ff3dee) との 135° グラデーションを「切り抜き完了」の象徴に使う
  • ヒーローのタイトルは「一瞬で消す」だけグラデーション化、他はフラット
  • スポットライト風の上からの淡い発光を入れて、撮影スタジオ感を出す
  • voice-scribe (mint cyan / 縦バー / オシロ) と clip-cast (amber / 横タイムライン / 編集機) に対して、 こちらは紫 / フォトスタジオ という温度・形ともに別軸

#実装の見どころ

#1. ライブラリ依存を最小限に薄く包む

removeBackground(file) を呼ぶだけで結果の Blob が返るくらい、 @imgly のライブラリは綺麗に出来ています。 bgRunner.ts で薄くラップして、

  • 動的import (_libPromise で1回だけロード)
  • progress コールバックで kind: load / fetch / compute / done を UI に流す
  • 結果の ImageBitmap から width/height を測る
  • 入力/出力サイズと推論時間を一緒に返す

の最低限だけ持たせました。

ts
const blob = await rb(input, {
 output: { format: "image/png", quality: 1 },
 progress: (key, current, total) => onProgress({ kind: "compute", ratio: current / total }),
})

これで API 側の都合が変わってもラップ層だけで吸収できます。

#2. before/after compare slider

被写体抽出の結果を見せるのに、左右並べるよりも 「同じ位置で1枚の画像が透ける」 体験のほうがインパクトが強い。 Pointer Events で実装した可動式スライダーで、ハンドルをドラッグするとマスクされた境界が左右に動きます。

実装ポイント:

  • 後ろに before 画像を常時表示
  • 前面に after 画像を width: ${pct}% の overflow:hidden コンテナで重ね、 内側の img を width: 100vw; max-width: none にして 位置がぶれない ようにする
  • ハンドルは pointermove で世界座標→相対座標→pct に変換

#3. backdrop chips で出力プレビューの色を切替

透過 PNG は、表示する背景色で印象が大きく変わります。 そこでステージの背景を チェッカー / 白 / 黒 / 紫 の4択でその場切替できるようにしました。 商品写真なら白、SNSアイコンなら紫、というふうにユーザーが「どこで使うか」を即座に確かめられます。

#4. 動的 import + ssr:false

Next.js 16 の Server Component から @imgly/background-removal を import するとSSR時に死にます。 ContactForm / VoiceScribe / ClipCast と同じく、loader でラップして dynamic({ ssr: false }) を強制しています。

tsx
const BGSnapApp = dynamic( => import("./BGSnapApp").then((m) => m.BGSnapApp), {
 ssr: false,
 loading: => null,
})

#苦労したところ

  • 初回モデルDLが ~80MB。 progress を見せないと「動かない」誤解を生むので、 推論前にローダーUIを出すフローを最初から組んだ。
  • 巨大画像 (>8000px) のメモリ占有。 短辺で5000pxを目安にFAQに明記。ライブラリ側にダウンサンプリングも入っているが、限界はある。
  • PNG出力の Blob 取り扱い。 BlobPart の型と ArrayBufferLike の所有権周りで一度ハマったが、 入出力とも Blob オブジェクトを直接受け渡す形に統一して回避。

#今後の拡張

  • 複数オブジェクト分離: 1枚に複数被写体がある場合に切り分ける選択UI
  • エッジ精度の改善モード: 別モデルへの切替トグル (精度↔速度のトレードオフ)
  • 影 (drop shadow) 復元オプション: アイコンや商品写真の自然さを上げる
  • バッチ処理: 複数ファイルをドロップ→キュー実行
  • clip-cast / voice-scribe との連携: 動画フレームから切り抜き → サムネ生成、字幕音声から無人ナレ動画への合体など

#このサービスから言えること

「メディア (音声・動画・画像) を扱う処理は全部する」 という主張を、3本目で実証しました。 これでサイトとしての立ち位置がはっきりした気がします。 「アップロードしないと使えない」現状のメディア系SaaSのほとんどは、 そろそろの代替が技術的に成立する時代に入ったということです。

[ ./next_action ]

読んだら、 BGSnap を実際に動かす。

この開発ログは BGSnap をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →