PageSourceSearch

https://webcade.fun/avatar.js?v=1025436e

js webcade.fun collected 2026-10-08 08:27:02 UTC 4,582 bytes, 96 lines download raw bytes

1// Player avatars, drawn rather than uploaded.
2//
3// Every wallet gets a distinct identicon derived from its own address, so a
4// profile picture exists from the first play with nothing to set up. A player
5// can instead pick one of the arcade sprites below. Nothing is uploaded, so
6// there is no storage, no hotlinking and no moderation surface.
7(() => {
8  // FNV-1a: small, stable, and identical everywhere — the avatar must not
9  // change between page loads or between devices.
10  function hash(str) {
11    let h = 0x811c9dc5;
12    for (let i = 0; i < str.length; i++) {
13      h ^= str.charCodeAt(i);
14      h = Math.imul(h, 0x01000193) >>> 0;
15    }
16    return h;
17  }
18
19  const PALETTE = [
20    '#ff2bd6', '#2bfff2', '#ffe12b', '#3bff9d', '#ff6a3b', '#b13bff',
21    '#3bd6ff', '#7dff3b', '#ff3b3b', '#ffb13b', '#6a8cff', '#ffd27a',
22  ];
23
24  // 8x8 sprites, one glyph per cabinet flavour. '.' is transparent.
25  const SPRITES = [
26    { name: 'GHOST',    c: '#ff3b3b', p: ['..1111..','.111111.','11011011','11111111','11111111','11111111','11111111','1.11.11.'] },
27    { name: 'INVADER',  c: '#3bff9d', p: ['..1..1..','...11...','..1111..','.11011..','11111111','1.1111.1','1.1..1.1','..11.11.'] },
28    { name: 'TILE',     c: '#ffd27a', p: ['11111111','1......1','1.1111.1','1.1..1.1','1.1..1.1','1.1111.1','1......1','11111111'] },
29    { name: 'LANDER',   c: '#cfcfe8', p: ['...11...','..1111..','.111111.','11111111','.1.11.1.','1.1111.1','1......1','.1....1.'] },
30    { name: 'BARREL',   c: '#ffb13b', p: ['.111111.','11111111','1.1111.1','11111111','1.1111.1','11111111','1.1111.1','.111111.'] },
31    { name: 'MUSHROOM', c: '#7dff3b', p: ['..1111..','.111111.','11111111','11011011','.111111.','...11...','...11...','..1111..'] },
32    { name: 'FLIPPER',  c: '#6a8cff', p: ['1......1','11....11','.11..11.','..1111..','..1111..','.11..11.','11....11','1......1'] },
33    { name: 'FROG',     c: '#3bff3b', p: ['1.1..1.1','11111111','1.1111.1','11111111','.111111.','1.1..1.1','1......1','11....11'] },
34    { name: 'PADDLE',   c: '#3bd6ff', p: ['........','........','...11...','........','11111111','11111111','........','........'] },
35    { name: 'CUBE',     c: '#ff8c1a', p: ['...11...','..1111..','.111111.','11111111','11111111','.111111.','..1111..','...11...'] },
36    { name: 'ROCKET',   c: '#ff2bd6', p: ['...11...','..1111..','..1111..','.111111.','11111111','1.1111.1','...11...','..1..1..'] },
37    { name: 'SNAKE',    c: '#7dffb0', p: ['1111....','1..1....','1..1111.','1.....1.','1111..1.','...1..1.','...1111.','........'] },
38  ];
39
40  // GitHub-style identicon: a 5x5 grid mirrored down the middle so it always
41  // reads as a face-like shape rather than noise.
42  function drawIdenticon(ctx, addr, size) {
43    const h = hash(String(addr));
44    const fg = PALETTE[h % PALETTE.length];
45    const bg = '#12122a';
46    const cell = size / 5;
47    ctx.fillStyle = bg;
48    ctx.fillRect(0, 0, size, size);
49    ctx.fillStyle = fg;
50    for (let x = 0; x < 3; x++) {
51      for (let y = 0; y < 5; y++) {
52        // one bit of the hash per cell, reused across the mirror
53        const bit = (hash(addr + ':' + x + ':' + y) >>> 3) & 1;
54        if (!bit) continue;
55        ctx.fillRect(Math.round(x * cell), Math.round(y * cell), Math.ceil(cell), Math.ceil(cell));
56        ctx.fillRect(Math.round((4 - x) * cell), Math.round(y * cell), Math.ceil(cell), Math.ceil(cell));
57      }
58    }
59  }
60
61  function drawSprite(ctx, index, size) {
62    const s = SPRITES[index];
63    if (!s) return;
64    const cell = size / 8;
65    ctx.fillStyle = '#12122a';
66    ctx.fillRect(0, 0, size, size);
67    ctx.fillStyle = s.c;
68    for (let y = 0; y < 8; y++) {
69      for (let x = 0; x < 8; x++) {
70        if (s.p[y][x] === '1') {
71          ctx.fillRect(Math.round(x * cell), Math.round(y * cell), Math.ceil(cell), Math.ceil(cell));
72        }
73      }
74    }
75  }
76
77  // Render into a canvas element. avatar === null means the generated identicon.
78  function render(canvas, addr, avatar) {
79    if (!canvas || !canvas.getContext) return;
80    const ctx = canvas.getContext('2d');
81    const size = canvas.width;
82    ctx.clearRect(0, 0, size, size);
83    if (avatar === null || avatar === undefined) drawIdenticon(ctx, addr, size);
84    else drawSprite(ctx, Number(avatar), size);
85  }
86
87  // an <img>-able data URL, for places that cannot host a live canvas
88  function dataUrl(addr, avatar, size) {
89    const c = document.createElement('canvas');
90    c.width = c.height = size || 32;
91    render(c, addr, avatar);
92    return c.toDataURL();
93  }
94
95  window.ARCADE_AVATAR = { render, dataUrl, SPRITES, count: SPRITES.length };
96})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.