Sunday, March 13, 2022

Show HN: HN Avatars in 357 bytes

The following self contained script annotates all HN comments on a page with username derived avatars. Simply paste it into the JS console of any HN page (F12).
  for(u of document.querySelectorAll('.hnuser'))for(u.parentElement.prepend(c=document.createElement('canvas')),x=c.getContext('2d'),c.width=c.height=14,s=u.innerText,r=1,i=28+s.length;i--;i<28?r>>>29>X*X/3+Y/2&&x.fillRect(6+2*X,2*Y,2,2)&x.fillRect(6-2*X,2*Y,2,2):r+=s.charCodeAt(i-28,x.fillStyle='#'+(r>>8&0xFFFFFF).toString(16)))r^=r<<13,r^=r>>>17,r^=r<<5,X=i&3,Y=i>>2
This was inspired by @frncsdrk's submission from earlier today [0].

The concept is to use HN usernames as the seed into a deterministic avatar generator. This generator is built from the famously simple xorshift32 PRNG, which both provides a random variable for the image generator steps, and "pseudo-hashes" the seed string to provide the initial PRNG state using a non-linear step (adding each codepoint - which is likely not very robust against collisions compared to proper hashing algorithms, but is simple and good enough).

The image generation part is a probability distribution with mirrored pixels... specifically: r>>>29 > X*X/3+Y/2 where the left side is 3 of the upper bits of the PRNG state (providing a random integer 0-7), and the right side is the squared distance from the centre for X + the linear distance from the top for Y. i.e the further from the top centre the pixel is, the less likely it will be filled, but linearly for Y and squared for X.

Un-golfed version:

    for (const u of document.querySelectorAll('.hnuser')) {
        const p=2;
        const c=document.createElement('canvas');
        const x=c.getContext('2d');
            c.width=p*7, c.height=p*7;
            u.parentElement.prepend(c);
        for (let s=u.innerText, r=1, i=28+s.length; i--;) {
            // xorshift32
            r^=r<<13, r^=r>>>17, r^=r<<5;
            const X=i&3, Y=i>>2;
            if (i >= 28) {
                // seed state
                r+=s.charCodeAt(i-28);
                x.fillStyle='#'+(r>>8&0xFFFFFF)
                .toString(16).padStart(0, 6);
            } else {
                // draw pixel
                if (r>>>29 > X*X/3+Y/2)
                x.fillRect(p*3+p*X, p*Y, p, p),
                x.fillRect(p*3-p*X, p*Y, p, p);
            }
        }
    }
Was fun to play with, but also surprisingly helpful in following discussions.

[0] https://ift.tt/rTzZnNO



from Hacker News https://ift.tt/X3eRxqg

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.