猫の手ツール

Algorithm & UI/UX

数学的に「揺らす」:
ピクセル置換による波紋の再現

このツールでは、単なる画像のオーバレイではなく、Canvasのピクセルデータを直接操作する「ディスプレイスメント・マッピング」という手法を採用しています。静止画に物理的な実在感を与えるためのアルゴリズムを解説します。

1. 正弦波(サイン波)による座標の歪み計算

波紋の正体は、中心からの距離に応じて「元画像のどの位置からピクセルを拾ってくるか」を計算した結果です。当ツールでは Math.sin を活用し、中心からの距離(dist)に基づいてピクセルの取得位置を意図的にずらしています。

この実装のメリットは、静止画でありながら光の屈折のような動的な変化をシミュレートできる点にあります。

// 波紋の歪み計算の核
const dist = Math.sqrt(dx * dx + dy * dy);

if (dist < maxDist && dist > 0) {
    // 正弦波による歪み量と、外側へ向かうほど弱まる減衰処理
    const amount = Math.sin(dist * freq * // 独自のチューニング値) * strength * (1 - dist / maxDist);
    
    // ベクトルに基づいて参照座標をオフセット
    sx = x + (dx / dist) * amount;
    sy = y + (dy / dist) * amount;
}

2. メモリ効率を重視したピクセルサンプリング

高解像度の画像を処理する際、Canvas APIの getImageDataputImageData を高頻度で回すとブラウザがフリーズする恐れがあります。そこで、計算用の「オフスクリーン・キャンバス」と「表示用キャンバス」を分離し、さらに全ピクセルを一括で TypedArray として操作する工夫を施しました。

ループ内での計算を最小限に抑え、ブラウザのメインスレッドを長時間占有しないように設計されています。

// 描画バッファの直接操作
const srcPixels = srcData.data;
const dstPixels = dstData.data;

for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
        // ここに座標歪みの計算処理が入ります
        
        // 浮動小数点数で計算された参照先を整数インデックスに変換
        const srcIdx = (Math.floor(sy) * w + Math.floor(sx)) * 4;
        const dstIdx = (y * w + x) * 4;

        // RGBA値を高速コピー
        dstPixels[dstIdx] = srcPixels[srcIdx];
        dstPixels[dstIdx + 1] = srcPixels[srcIdx + 1];
        dstPixels[dstIdx + 2] = srcPixels[srcIdx + 2];
        dstPixels[dstIdx + 3] = srcPixels[srcIdx + 3];
    }
}

3. iPhone(HEIC形式)への動的対応と最適化

iPhoneで撮影された写真は標準でHEIC形式ですが、ブラウザのCanvasはこれを直接解釈できません。当ツールでは、必要なときだけ heic2any ライブラリを動的にインポートすることで、初期ロードの軽量化と、iPhoneユーザーの利便性を両立させています。

また、高解像度すぎる画像はブラウザのメモリ制限に抵触するため、一定以上のサイズはアスペクト比を維持しながら自動リサイズする安全装置を実装しています。

// HEIC変換の動的インポートとリサイズ処理
if (file.name.toLowerCase().match(/\.(heic|heif)$/)) {
    // 必要な時だけ重いライブラリをロードする戦略
    const heicModule = await import('heic2any');
    // ...変換処理...
}

// 処理可能な最大解像度の制限
if (w > MAX_SIZE_LIMIT || h > MAX_SIZE_LIMIT) {
    const scale = MAX_SIZE_LIMIT / Math.max(w, h);
    // 独自のチューニング値に基づいたリサイズ計算
}

Developer's Note

「猫の手ツール」のコンセプトである「ブラウザ完結・プライバシー保護」を貫くため、サーバーサイドの画像処理ライブラリに頼らず、すべての物理シミュレーションを JavaScript で実装することにこだわりました。

波の「減衰(外側に行くほど弱まる)」のパラメータ調整が最も難航した部分で、線形の減衰だけでなく、少し独自の係数を掛けることで、より「水たまりに石を落とした時」のような自然な揺らぎに近づけています。ぜひ、お手持ちの風景写真でその質感を試してみてください。