▍AI Dev Lab
PicFlip

PicFlip ができるまで — libheif で HEIC を変換する

heic2any (libheif WASM) と Canvas API で HEIC / JPG / PNG / WebP のクロス変換をブラウザ完結で実装したWebサービスの設計記録。バッチ・品質スライダー・暗室セーフライト演出までを解説します。

·decision改善·stage公開中

PicFlip は、画像形式 (HEIC / JPG / PNG / WebP) を アップロードせずに 変換するWebサービスです。 voice-scribe / clip-cast / bg-snap / text-pluck / pdf-anvil / pixel-lift に続く、メディア処理ラボの 7本目。 「iPhone の HEIC を Windows / Android で開ける形式にしたい」 という universal な需要をで解く。

#なぜこの形にしたか

「HEIC 変換」 「PNG JPG 変換」 は日本語圏でも巨大な検索需要で、 既存サービスはほぼ全部アップロード前提です。 個人写真や家族の写真をアップロードしない代替を出せれば、 説明不要の差別化になります。

heic2any は libheif (Apple HEIC 仕様のオープン実装) を WebAssembly でブラウザに持ち込み、 数行で HEIC を Blob にデコードできる。 ここから先は Canvas で JPG / PNG / WebP のクロス変換ができる。 全部ブラウザの中だけで完結します。

#見た目の方針

暗室セーフライト × ローズレッド。

  • ほぼ黒 (#0a0608) を背景に、 ローズレッド (#f43f5e) を主アクセント、 ピンク (#fda4af) をハイライトに
  • 「フィルム現像の安全光」 をモチーフにして、 既存6本 (mint / amber / violet / cobalt / lime / gold) と完全に別の温度帯
  • 形式変換 = 「セーフライトの下で別の媒体に焼き付け直す」 という暗室メタファーをUI全体に貫いている
  • アクセントを linear-gradient(135deg, rose → pink) でグラデーション化することで、 単色ではなく色の遷移自体が「形式を切り替える」 動作を表現

#実装の見どころ

#1. HEIC → JPG/PNG/WebP の分岐

heic2any は toType: image/jpeg | image/png のみネイティブ対応で、 WebP は出力できない。 そこで:

ts
const intermediate = toFormat === "webp" ? "image/jpeg" : MIME_FOR_FORMAT[toFormat]
const out = await heic2any({ blob: file, toType: intermediate, quality })
if (toFormat !== "webp") return out
return await reencodeViaCanvas(out, "webp", quality)

HEIC → JPEG (libheif) → WebP (Canvas) の2段経路で WebP もカバーします。

#2. JPG ↔ PNG ↔ WebP のクロス変換 (Canvas)

非HEICの入力は createImageBitmap で ImageBitmap にデコードし、 Canvas 経由で目的形式に再エンコード。 JPEG 出力時だけ白背景を敷いてアルファチャネル落ちを目立たせない処理を入れています。

ts
if (toFormat === "jpeg") {
 ctx.fillStyle = "#ffffff"
 ctx.fillRect(0, 0, canvas.width, canvas.height)
}
ctx.drawImage(bitmap, 0, 0)
canvas.toBlob(resolve, MIME_FOR_FORMAT[toFormat], quality)

#3. 一括バッチ処理

convertBatch(files, format, quality, onProgress) で複数ファイルを順次処理。 失敗したファイルは failures 配列に蓄積され、 成功分とは別にエラーリストとして UI に表示します。 「10ファイル投げて9枚成功した」 という現実的なケースで、 成功分を捨てずに保存できるようにしました。

#4. quality スライダー (lossy 形式のみ)

JPG / WebP は quality 0.1-1.0 で書き出しサイズが大きく変わります。 PNG は常に lossless なので、 出力形式を PNG に切り替えると quality スライダーは自動で隠れます。 「設定が見えていれば触れる、効かない設定は見せない」 原則です。

#5. EXIF の扱い

Canvas を介する変換だと EXIF はほぼ落ちます。 これは多くのユーザーにとってむしろ望ましい挙動 (位置情報や撮影機種の漏洩を防げる) ですが、 「撮影日を残したい」 ケースもあるので、 EXIF 保持 / 完全 strip のトグルを今後の拡張として FAQ に予告しています。

#苦労したところ

  • heic2any のメモリリーク疑惑: 大きい HEIC を連続変換すると WASM ヒープが伸びる傾向があった。 ファイル単位で再 import して回避する手も検討。 v1 では実用範囲なので保留。
  • iOS Safari の image/heic MIME 認識: ファイルピッカーで image/* を指定しても HEIC が選択肢に出ないケースがあるため、 input の accept に .heic,.heif を明示。
  • WebP 経路の品質ロス: HEIC → JPEG → WebP の2段経路は、 JPEGデコード時にロスが出る。 高品質を要求するなら HEIC→PNG→WebP の lossless 中間案も将来選択肢に。

#今後の拡張

  • AVIF / TIFF 対応 (libavif / libtiff WASM)
  • RAW 対応 (libraw WASM、 ただし依存が重い)
  • EXIF 保持 / 完全 strip トグル + EXIF ビューワ
  • リサイズ + 形式変換の一気通貫 (PixelLift と統合する余地あり)
  • clip-cast との連携: 動画フレームを抽出して PicFlip に流し込む
  • bg-snap / PixelLift との連携導線: PicFlip で JPG にしてから背景透過、高解像度化、までを1ページで

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

メディア処理ラボの 7 本:

  • voice-scribe / clip-cast / bg-snap / text-pluck / pdf-anvil / pixel-lift / pic-flip

「形式変換」 という一見軽い領域も含めて、 メディアまわりの実用ニーズを 7方向でカバーできるようになりました。 という制約だけ守れば、 デスクトップアプリ並みの実用品を毎週でも生み出せる、という実感が積み上がっています。

[ ./next_action ]

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

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

[ ./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 →