Created
August 25, 2023 15:59
-
-
Save katspaugh/3037b44717c61ca5010299c70ae0ec1b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generate unique avatars based on Ethereum addresses. | |
// It uses celluar automata to generate the avatars which are unique for each address. | |
// Define the color pallets. | |
// Each pallet is an array of 3 colors: background, foreground, and highlight. | |
// The colors are in the RGB format, and we use soft pastel colors. | |
const palettes = [ | |
// Palette 1 | |
[ | |
[255, 182, 193], // Background: Light Pink | |
[219, 112, 147], // Foreground: Pale Violet Red | |
[255, 130, 171], // Highlight: Lighter Pale Violet Red | |
], | |
// Palette 2 | |
[ | |
[173, 216, 230], // Background: Light Blue | |
[70, 130, 180], // Foreground: Steel Blue | |
[100, 149, 237], // Highlight: Lighter Steel Blue | |
], | |
// Palette 3 | |
[ | |
[240, 230, 140], // Background: Khaki | |
[189, 183, 107], // Foreground: Dark Khaki | |
[238, 232, 170], // Highlight: Lighter Dark Khaki | |
], | |
// Palette 4 | |
[ | |
[152, 251, 152], // Background: Pale Green | |
[60, 179, 113], // Foreground: Medium Sea Green | |
[84, 255, 159], // Highlight: Lighter Medium Sea Green | |
], | |
// Palette 5 | |
[ | |
[255, 228, 225], // Background: Misty Rose | |
[255, 105, 180], // Foreground: Hot Pink | |
[255, 182, 193], // Highlight: Lighter Hot Pink | |
], | |
// Palette 6 | |
[ | |
[240, 248, 255], // Background: Alice Blue | |
[100, 149, 237], // Foreground: Cornflower Blue | |
[135, 206, 250], // Highlight: Lighter Cornflower Blue | |
], | |
// Palette 7 | |
[ | |
[255, 240, 245], // Background: Lavender Blush | |
[219, 112, 147], // Foreground: Pale Violet Red | |
[255, 182, 193], // Highlight: Lighter Pale Violet Red | |
], | |
// Palette 8 | |
[ | |
[250, 235, 215], // Background: Antique White | |
[222, 184, 135], // Foreground: Burlywood | |
[255, 211, 155], // Highlight: Lighter Burlywood | |
], | |
// Palette 9 | |
[ | |
[255, 222, 173], // Background: Navajo White | |
[210, 105, 30], // Foreground: Chocolate | |
[255, 140, 64], // Highlight: Lighter Chocolate | |
], | |
// Palette 10 | |
[ | |
[245, 245, 220], // Background: Beige | |
[188, 143, 143], // Foreground: Rosy Brown | |
[255, 193, 193], // Highlight: Lighter Rosy Brown | |
], | |
// Palette 11 | |
[ | |
[255, 239, 213], // Background: Papaya Whip | |
[255, 165, 0], // Foreground: Orange | |
[255, 193, 37], // Highlight: Lighter Orange | |
], | |
// Palette 12 | |
[ | |
[245, 255, 250], // Background: Mint Cream | |
[32, 178, 170], // Foreground: Light Sea Green | |
[64, 224, 208], // Highlight: Lighter Light Sea Green | |
], | |
// Palette 13 | |
[ | |
[250, 250, 210], // Background: Light Goldenrod Yellow | |
[238, 232, 170], // Foreground: Pale Goldenrod | |
[255, 250, 205], // Highlight: Lighter Pale Goldenrod | |
], | |
// Palette 14 | |
[ | |
[255, 248, 220], // Background: Cornsilk | |
[218, 165, 32], // Foreground: Goldenrod | |
[255, 215, 64], // Highlight: Lighter Goldenrod | |
], | |
// Palette 15 | |
[ | |
[255, 228, 196], // Background: Bisque | |
[205, 133, 63], // Foreground: Peru | |
[255, 160, 122], // Highlight: Lighter Peru | |
], | |
// Palette 16 | |
[ | |
[255, 218, 185], // Background: Peach Puff | |
[233, 150, 122], // Foreground: Dark Salmon | |
[255, 160, 122], // Highlight: Lighter Dark Salmon | |
], | |
// Palette 17 | |
[ | |
[255, 245, 238], // Background: Seashell | |
[210, 105, 30], // Foreground: Chocolate | |
[255, 140, 64], // Highlight: Lighter Chocolate | |
], | |
// Palette 18 | |
[ | |
[245, 245, 245], // Background: White Smoke | |
[192, 192, 192], // Foreground: Silver | |
[211, 211, 211], // Highlight: Lighter Silver | |
], | |
// Palette 19 | |
[ | |
[253, 245, 230], // Background: Old Lace | |
[205, 92, 92], // Foreground: Indian Red | |
[255, 106, 106], // Highlight: Lighter Indian Red | |
], | |
// Palette 20 | |
[ | |
[250, 240, 230], // Background: Linen | |
[139, 69, 19], // Foreground: Saddle Brown | |
[160, 82, 45], // Highlight: Lighter Saddle Brown | |
], | |
] | |
// We will generate a pallet based on the address. | |
// Next, define the number of iterations. | |
// The more iterations, the more detailed the image will be. | |
// However, the more iterations, the longer it will take to generate the image. | |
// The number of iterations must be a power of 2. | |
const iterations = 256 | |
// Next, define the number of cells. | |
// The more cells, the more detailed the image will be. | |
// However, the more cells, the longer it will take to generate the image. | |
// The number of cells must be a power of 2. | |
const cellNumber = 196 | |
// Next, define the celluar automata rules. | |
// The rules are defined as a 2D array. | |
// The first dimension is the number of neighbors. | |
// The second dimension is the number of states. | |
// The value is the new state. | |
// For example, if the cell has 2 neighbors, and the current state is 1, then the new state is 0. | |
// The rules must be a power of 2. | |
const rules = [ | |
[0, 1], | |
[1, 0], | |
[1, 1], | |
[1, 0], | |
] | |
// Now, define the flood fill threshold. | |
// The flood fill algorithm will fill in holes in the image. | |
// The threshold is the maximum size of a hole that will be filled. | |
// The threshold must be a power of 2. | |
const threshold = 16 | |
// Now, define the celluar automata function. | |
// This function will generate an array of cells. | |
// Each cell is a number between 0 and 1. | |
// The function takes a seed as an argument. | |
// The seed is a string. | |
// The function returns an array of cells. | |
const celluarAutomata = (ethereumAddress) => { | |
const binaryAddress = Number(ethereumAddress, 16).toString(2) | |
const cells = new Array(cellNumber) | |
// Next, create an array of cells according to the rules defined above. | |
for (let i = 0; i < cellNumber; i++) { | |
cells[i] = Number(binaryAddress[i % binaryAddress.length]) | |
} | |
// Next, apply the rules to the cells. | |
for (let i = 0; i < iterations; i++) { | |
const prev = cells.slice() | |
for (let j = 0; j < cellNumber; j++) { | |
const neighbors = prev[(j - 1 + cellNumber) % cellNumber] + prev[j] + prev[(j + 1) % cellNumber] | |
cells[j] = rules[neighbors][prev[j]] | |
} | |
} | |
// Next, return the cells. | |
return cells | |
} | |
function generatePalette(ethereumAddress) { | |
const seed = Number('0x' + ethereumAddress.slice(12, 19), 16) | |
return palettes[seed % palettes.length] | |
} | |
function draw(cells, palette) { | |
const canvas = document.createElement('canvas') | |
const context = canvas.getContext('2d') | |
const size = Math.round(Math.sqrt(cellNumber)) | |
canvas.width = canvas.height = size | |
canvas.style.width = canvas.style.height = `${300}px` | |
canvas.style.imageRendering = 'pixelated' | |
canvas.style.border = '1px solid #ddd' | |
const imageData = context.createImageData(size, size) | |
const data = imageData.data | |
// Draw the first half of the cells. | |
for (let i = 0; i < cellNumber; i++) { | |
const x = i % size | |
const y = Math.floor(i / size) | |
const neighbors = cells[(i - 1 + cellNumber) % cellNumber] + cells[i] + cells[(i + 1) % cellNumber] | |
const color = cells[i] ? neighbors > 2 ? 2 : 1 : 0 | |
data[i * 4 + 0] = palette[color][0] | |
data[i * 4 + 1] = palette[color][1] | |
data[i * 4 + 2] = palette[color][2] | |
data[i * 4 + 3] = 255 | |
} | |
// Now take the left side of the imageData, mirror it, and draw it on the right side. | |
data.forEach((value, index) => { | |
if (index % 4 === 0) { | |
const x = index / 4 % size | |
const y = Math.floor(index / 4 / size) | |
const mirrorX = size - x - 1 | |
const mirrorIndex = (mirrorX + y * size) * 4 | |
data[index] = data[mirrorIndex] | |
data[index + 1] = data[mirrorIndex + 1] | |
data[index + 2] = data[mirrorIndex + 2] | |
data[index + 3] = data[mirrorIndex + 3] | |
} | |
}) | |
context.putImageData(imageData, 0, 0) | |
document.body.appendChild(canvas) | |
} | |
function main(address) { | |
draw(celluarAutomata(address), generatePalette(address)) | |
} | |
main('0xA858DDc0445d8131daC4d1DE01f834ffcbA52Ef1') | |
main('0x9d94ef33e7f8087117f85b3ff7b1d8f27e4053d5') | |
main('0x474e5Ded6b5D078163BFB8F6dBa355C3aA5478C8') | |
main('0x85C9f5aA0F82A531087a356a55623Cf05E7Bb895') | |
main('0xA77DE01e157f9f57C7c4A326eeE9C4874D0598b6') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment