🖼️

Identiconの楽な作り方

に公開

サマリー

Identiconとかいうものはハッシュ化やライフゲームを使うやたら生成が難しいものとして扱われていますが別にそんなことはないという話です。

ユーザー名などアルファベットの文字列は1~26までの数字が並ぶ1次元の数列とみなせるので、それの平均や階差の平均、標準偏差や歪度を使えば比較的簡単に作れます。

const identicon = async (handle_name) => {
    const alphaVal = (s) => (s.toLowerCase().charCodeAt(0) - 97) / 25
    const alphabets = handle_name.split("").filter(_ => _!="_" && _!="-" && !(parseInt(_)+1))
    const mapped = alphabets.map(alphaVal)
    const mean = mapped.reduce((a,b) => a+b) / mapped.length
    const diff = mapped.map((_,b) => mapped[b+1] - mapped[b]).slice(0,-1)
    const diffmean = diff.reduce((a,b) => a+b) / diff.length
    const diffstd = Math.sqrt(diff.map(_ => (_ - diffmean) * (_ - diffmean)).reduce((a,b) => a+b) / diff.length)
    const meandeg = -(mean - 0.5) * 240, stddeg = diffstd * 240

    const query = `https://placehold.jp/3d4070/ffffff/384x384.jpg?
        text=%E3%80%80&css=%7B%22background%22%3A%22%20
        linear-gradient(-${stddeg}deg%2C%20rgba(50%2C65%2C88%2C1)%200%25%2C%20rgba(50%2C65%2C88%2C1)%2050%25%2C%20rgba(14%2C23%2C43%2C1)%2050%25)%22%2C%22
        filter%22%3A%22%20hue-rotate(${meandeg}deg)%22%7D`
    return await fetch(query)
};

赤裸々に公開してしまいますが、Keik.orgのIdenticon生成ロジックはこんな感じです。
(βテスト時はこれでしたが、今はさすがにplacehold.jpを使わない仕組みになっています)

これは比較的単純なロジックですが、現実的に被りの可能性を限りなく減らしたい場合、歪度で曲線性をつけたり尖度でそのサイズを伸縮したり…といった発想も必要かと思います。

あるいはヒストグラムやその一般化正規分布などによる近似自体をアイコンにしたり…

まあともあれSHA256は計算に負荷がかかるので他の方法を考えよう、という話でした。

Discussion