Skip to content

Instantly share code, notes, and snippets.

@depperm
Last active July 15, 2025 17:36
Show Gist options
  • Save depperm/f521ee5260d063c6747eddb224f4bf25 to your computer and use it in GitHub Desktop.
Save depperm/f521ee5260d063c6747eddb224f4bf25 to your computer and use it in GitHub Desktop.
Identicon
function toHex(a, length = 2) {
return a.toString(16).padStart(length, '0')
}
function generateContrib(){
//const hash = document.getElementById('seed').value.murmurHash()
const hash = document.getElementById('seed').value.cyrb53Hash()
const rand = mulberry32(parseInt(hash, 16))
const gray = 'ececef'
const baseColor = hash.substring(6,8)+hash.substring(0,2)+hash.substring(3,5)
let [gr,gg,gb] = [parseInt(gray.substring(0,2),16), parseInt(gray.substring(2,4),16), parseInt(gray.substring(4,6),16)]
let [br,bg,bb] = [parseInt(baseColor.substring(0,2),16), parseInt(baseColor.substring(2,4),16), parseInt(baseColor.substring(4,6),16)]
let light = [Math.floor((gr+br)/2), Math.floor((gg+bg)/2), Math.floor((gb+bb)/2)]
let lightColor = toHex(light[0])+toHex(light[1])+toHex(light[2])
let darker = toHex(Math.max(br-50,0))+toHex(Math.max(bg-50,0))+toHex(Math.max(bb-50,0))
let darkest = toHex(Math.max(br-100,0))+toHex(Math.max(bg-100,0))+toHex(Math.max(bb-100,0))
const c = document.getElementById('identicon')
const ctx = c.getContext('2d')
ctx.clearRect(0,0,c.width,c.height)
const squareSize = 14
const spacing = c.width/100
const totalSize = (c.width - (spacing*(squareSize-1)))/squareSize
for(let r=0;r<squareSize;r++){
for(let c=0;c<squareSize;c++){
// apply bell curve toward lower end
let q = Math.min(Math.floor(rand()*50), Math.floor(rand()*50))
if (q < 10) {
ctx.fillStyle=`#${gray}`
} else if (q < 20) {
ctx.fillStyle=`#${lightColor}`
} else if (q < 30) {
ctx.fillStyle=`#${baseColor}`
} else if (q < 40) {
ctx.fillStyle=`#${darker}`
} else {
ctx.fillStyle=`#${darkest}`
}
ctx.beginPath()
ctx.rect((c*totalSize)+(spacing*c), (r*totalSize)+(r*spacing), totalSize, totalSize)
ctx.fill()
}
}
}
function generateIdenticon(){
const identiconSize = document.getElementById('size').value
document.getElementById('identicon').height = identiconSize
document.getElementById('identicon').width = identiconSize
generateContrib()
}
generateIdenticon()
function generateGit(){
//const hash = document.getElementById('seed').value.murmurHash()
const hash = document.getElementById('seed').value.cyrb53Hash()
const color1 = `#${hash.substring(2,8)}`
const color2 = `#${hash.substring(5,8)}`
const c = document.getElementById('identicon')
const ctx = c.getContext('2d')
const blockSize = c.width/5
ctx.fillStyle=color1
ctx.beginPath()
ctx.rect(0,0,c.width,c.height)
ctx.fill()
for(let row=0;row<15;row++){
let shouldDraw = parseInt(hash.substring(Math.floor(row/2),Math.floor(row/2)+1),16)%2 === 0
ctx.beginPath()
ctx.fillStyle = shouldDraw ? color1 : color2
if(row<5){//middle col
ctx.rect(2*blockSize, row*blockSize, blockSize, blockSize)
} else if (row<10) {//border middle cols
ctx.rect(1*blockSize, (row-5)*blockSize, blockSize, blockSize)
ctx.fill()
//far side
ctx.beginPath()
ctx.rect(3*blockSize, (row-5)*blockSize, blockSize, blockSize)
} else {//edge cols
ctx.rect(0*blockSize, (row-10)*blockSize, blockSize, blockSize)
ctx.fill()
//far side
ctx.beginPath()
ctx.rect(4*blockSize, (row-10)*blockSize, blockSize, blockSize)
}
ctx.fill()
}
}
function generateIdenticon(){
const identiconSize = document.getElementById('size').value
document.getElementById('identicon').height = identiconSize
document.getElementById('identicon').width = identiconSize
generateGit()
}
generateIdenticon()
<canvas id="identicon"></canvas>
<br/>
<label for="seed">Seed</label>
<input id="seed" type="text" value="test" onkeyup="generateIdenticon()">
<br>
<label for="size">Size</label>
<input id="size" type="number" value="300" onchange="generateIdenticon()" onkeyup="generateIdenticon()">
<br>
<i>Best values are divisible by 4 and 5. Otherwise there is a chance of line artifacts.</i>
function drawJdent(ctx, blockSize, shape, position, rotation) {
const halfBlock = Math.floor(blockSize/2)
let offsets = []
let initial = []
switch(position){
case 0: // corner
offsets = [[0,0],[blockSize*3,0],[blockSize*3,blockSize*3],[0,blockSize*3]]
switch(shape){
case 0: // diamond (tes)
offsets.forEach(offset => {
ctx.beginPath()
ctx.moveTo(halfBlock+offset[0], offset[1])
ctx.lineTo(blockSize+offset[0], halfBlock+offset[1])
ctx.lineTo(halfBlock+offset[0], blockSize+offset[1])
ctx.lineTo(offset[0], halfBlock+offset[1])
ctx.closePath()
ctx.fill()
})
break;
case 1: // circle (testdes)
offsets.forEach(offset => {
ctx.beginPath()
ctx.arc(halfBlock+offset[0], halfBlock+offset[1], blockSize*.325,0,2*Math.PI)
ctx.fill()
})
break
case 2: // large tri (test)
case 3: // narrow tri (testd)
offsets.forEach(offset => {
if (shape === 2) {
initial = [
[rotation<2?blockSize:0, [0,3].includes(rotation)?blockSize:0],
[[0,3].includes(rotation)?blockSize:0, rotation<2?0:blockSize],
[rotation<2?0:blockSize, [0,3].includes(rotation)?0:blockSize]
]
} else {
initial = [
[rotation%2===0?halfBlock:(rotation===1?blockSize:0), [1,3].includes(rotation)?halfBlock:(rotation===0?0:blockSize)],
[[0,3].includes(rotation)?0:blockSize, rotation<2?0:blockSize],
[rotation<2?0:blockSize, [0,3].includes(rotation)?blockSize:0]
]
}
ctx.beginPath()
ctx.moveTo(initial[0][0] + offset[0],initial[0][1] + offset[1])
ctx.lineTo(initial[1][0] + offset[0],initial[1][1] + offset[1])
ctx.lineTo(initial[2][0] + offset[0],initial[2][1] + offset[1])
ctx.closePath()
ctx.fill()
rotation += (shape === 2 ? -1 : 1)
if (rotation>3) rotation = 0
else if(rotation<0) rotation = 3
})
break
}
break;
case 1: // not center/corner
offsets = [[blockSize, 0],[blockSize*2, 0],[blockSize*3, blockSize],[blockSize*3, blockSize*2],[blockSize*2, blockSize*3],[blockSize, blockSize*3],[0, blockSize*2],[0, blockSize]]
let skip = 0
switch(shape){
case 0: // circle (testd)
offsets.forEach(offset=>{
ctx.beginPath()
ctx.arc(halfBlock+offset[0], halfBlock+offset[1], blockSize*.325, 0, 2*Math.PI)
ctx.fill()
})
break;
case 1: // diamond (test)
offsets.forEach(offset=>{
ctx.beginPath()
ctx.moveTo(halfBlock+offset[0], offset[1])
ctx.lineTo(offset[0], halfBlock+offset[1])
ctx.lineTo(halfBlock+offset[0], blockSize+offset[1])
ctx.lineTo(blockSize+offset[0], halfBlock+offset[1])
ctx.fill()
})
break
case 2: // lg tri (dse)
case 3: // sm tri (ds)
offsets.forEach(offset => {
initial = [
[rotation < 2 ? 0 : blockSize, [0,3].includes(rotation) ? blockSize : 0],
[[0,3].includes(rotation) ? 0 : blockSize, rotation < 2 ? 0 : blockSize],
[[0,3].includes(rotation) ? blockSize : 0, rotation < 2 ? blockSize : 0]
]
if (shape === 3) {
initial[1] = [rotation%2!==0 ? halfBlock : (rotation === 0 ? 0 : blockSize), rotation %2===0 ? halfBlock : (rotation === 1?0:blockSize)]
}
ctx.beginPath()
ctx.moveTo(initial[0][0]+offset[0], initial[0][1]+offset[1])
ctx.lineTo(initial[1][0]+offset[0], initial[1][1]+offset[1])
ctx.lineTo(initial[2][0]+offset[0], initial[2][1]+offset[1])
ctx.fill()
if (skip % 2 === 0) {
rotation += 1
if(rotation > 3) rotation = 0
}
skip += 1
})
break
}
break;
case 2: // center
offsets = [[blockSize,blockSize],[blockSize*2,blockSize],[blockSize*2,blockSize*2],[blockSize,blockSize*2]]
// is inverted
if ([0,2,4,6,8,10,13,14].includes(shape)) {
ctx.beginPath()
ctx.rect(blockSize,blockSize,blockSize*2,blockSize*2)
ctx.fill()
ctx.fillStyle='white'
}
const t = Math.sin(45) * blockSize * .4
switch(shape){
case 0: // invert float square center (tes)
case 1: // float square center (d)
ctx.beginPath()
ctx.rect(blockSize+.5*blockSize, blockSize+.5*blockSize, blockSize*.4, blockSize*.4)
ctx.rect(blockSize*2+.1*blockSize, blockSize+.5*blockSize, blockSize*.4, blockSize*.4)
ctx.rect(blockSize*2+.1*blockSize, blockSize*2+.1*blockSize, blockSize*.4, blockSize*.4)
ctx.rect(blockSize+.5*blockSize, blockSize*2+.1*blockSize, blockSize*.4, blockSize*.4)
ctx.fill()
break
case 2: // invert corner (,)
case 3: // corner (m)
ctx.beginPath()
ctx.moveTo(blockSize*2,blockSize*2-t)
ctx.lineTo(blockSize*2+t,blockSize*2)
ctx.lineTo(blockSize*2,blockSize*2+t)
ctx.lineTo(blockSize*2-t,blockSize*2)
ctx.closePath()
ctx.fill()
break
case 4: // invert float circle (e)
case 5: // float circle (j)
ctx.beginPath()
ctx.arc(blockSize*1.75,blockSize*1.75,blockSize*.2,0,2*Math.PI)
ctx.fill()
ctx.beginPath()
ctx.arc(blockSize*2.25,blockSize*1.75,blockSize*.2,0,2*Math.PI)
ctx.fill()
ctx.beginPath()
ctx.arc(blockSize*1.75,blockSize*2.25,blockSize*.2,0,2*Math.PI)
ctx.fill()
ctx.beginPath()
ctx.arc(blockSize*2.25,blockSize*2.25,blockSize*.2,0,2*Math.PI)
ctx.fill()
break
case 6: // invert narrow tri (a)
case 7: // narrow tri (i)
ctx.beginPath()
ctx.moveTo(blockSize*1.5, blockSize*2)
ctx.lineTo(blockSize*2, blockSize*1.15)
ctx.lineTo(blockSize*2, blockSize*1.5)
ctx.lineTo(blockSize*2.85, blockSize*2)
ctx.lineTo(blockSize*2.5, blockSize*2)
ctx.lineTo(blockSize*2, blockSize*2.85)
ctx.lineTo(blockSize*2, blockSize*2.5)
ctx.lineTo(blockSize*1.15, blockSize*2)
ctx.closePath()
ctx.fill()
break
case 8: // invert star (f)
case 9: // star (x)
ctx.beginPath()
ctx.moveTo(blockSize*1.4, blockSize*1.4)
ctx.lineTo(blockSize*2, blockSize*1.7)
ctx.lineTo(blockSize*2.6, blockSize*1.4)
ctx.lineTo(blockSize*2.3, blockSize*2)
ctx.lineTo(blockSize*2.6, blockSize*2.6)
ctx.lineTo(blockSize*2, blockSize*2.3)
ctx.lineTo(blockSize*1.4, blockSize*2.6)
ctx.lineTo(blockSize*1.7, blockSize*2)
ctx.closePath()
ctx.fill()
break
case 10: // invert rotated squares (1)
offsets = [[blockSize*2,blockSize*2],[blockSize*3-t,blockSize*2],[blockSize*3-t,blockSize*3-t],[blockSize*2,blockSize*3-t]]
offsets.forEach(offset=>{
ctx.beginPath()
ctx.moveTo(offset[0]-2*t,offset[1]-t)
ctx.lineTo(offset[0]-t,offset[1]-2*t)
ctx.lineTo(offset[0],offset[1]-t)
ctx.lineTo(offset[0]-t,offset[1])
ctx.closePath()
ctx.fill()
})
break
case 11: // circle quarter (h)
ctx.beginPath()
ctx.arc(blockSize*2, blockSize*2, blockSize*.5, 0,2*Math.PI)
ctx.fill()
break
case 12: // larger square (s)
ctx.beginPath()
ctx.rect(blockSize*1.2, blockSize*1.2, blockSize*1.6, blockSize*1.6)
ctx.fill()
break
case 13: // invert mini tri (t)
const base =.5
const height = .5
const padding = .1
ctx.beginPath()
ctx.moveTo(blockSize*(2-padding-base),blockSize*(2-height-padding))
ctx.lineTo(blockSize*(2-padding),blockSize*(2-height-padding))
ctx.lineTo(blockSize*(2-(base/2)-padding),blockSize*(2-padding))
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(blockSize*(2+padding+height),blockSize*(2-padding-base))
ctx.lineTo(blockSize*(2+padding+height),blockSize*(2-padding))
ctx.lineTo(blockSize*(2+padding),blockSize*(2-(base/2)-padding))
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(blockSize*(2+padding),blockSize*(2+padding+height))
ctx.lineTo(blockSize*(2+padding+base),blockSize*(2+padding+height))
ctx.lineTo(blockSize*(2+(base/2)+padding),blockSize*(2+padding))
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(blockSize*(2-padding-height),blockSize*(2+padding))
ctx.lineTo(blockSize*(2-padding-height),blockSize*(2+padding+base))
ctx.lineTo(blockSize*(2-padding),blockSize*(2+(base/2)+padding))
ctx.closePath()
ctx.fill()
break
case 14: // invert float square outer (te)
case 15: // float square outer (34)
ctx.beginPath()
ctx.rect(blockSize+.1*blockSize, blockSize+.1*blockSize, blockSize*.4, blockSize*.4)
ctx.rect(blockSize*2+.5*blockSize, blockSize+.1*blockSize, blockSize*.4, blockSize*.4)
ctx.rect(blockSize*2+.5*blockSize, blockSize*2+.5*blockSize, blockSize*.4, blockSize*.4)
ctx.rect(blockSize+.1*blockSize, blockSize*2+.5*blockSize, blockSize*.4, blockSize*.4)
ctx.fill()
break
default:
alert("Shouldn't reach this point:", shape)
break
}
break;
}
}
function generateJdent(){
const hash = document.getElementById('seed').value.murmurHash()
// const hash = document.getElementById('seed').value.cyrb53Hash()
const color1 = `#${hash.substring(0,3)}`
const color2 = `#${hash.substring(2,5)}`
const color3 = `#${hash.substring(5,8)}`
const c = document.getElementById('identicon')
const ctx = c.getContext('2d')
ctx.clearRect(0,0,c.width,c.height)
const blockSize = Math.floor(c.width/4)
let invert,shape,rotation
//corner
ctx.fillStyle = color1
shape = Math.floor(parseInt(hash.substring(0,1),16)/4)
rotation = Math.floor(parseInt(hash.substring(3,4),16)/4)
drawJdent(ctx, blockSize, shape, 0, rotation)
// not center/corner
ctx.fillStyle = color2
shape = Math.floor(parseInt(hash.substring(2,3),16)/4)
rotation = Math.floor(parseInt(hash.substring(1,2),16)/4)
drawJdent(ctx, blockSize, shape, 1, rotation)
//center
ctx.fillStyle = color3
// only know 14 shapes
shape = parseInt(hash.substring(4,5),16)
drawJdent(ctx, blockSize, shape, 2, rotation)
}
function generateIdenticon(){
const identiconSize = document.getElementById('size').value
document.getElementById('identicon').height = identiconSize
document.getElementById('identicon').width = identiconSize
generateJdent()
}
generateIdenticon()
function drawSmallTriTL(ctx,blockSize,info){
ctx.moveTo(blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize/2+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], blockSize/2+blockSize*info.offset[1])
ctx.closePath()
}
function drawSmallTriBL(ctx,blockSize,info){
ctx.moveTo(blockSize*info.offset[0], blockSize/2+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize/2+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.closePath()
}
function drawSmallTriTR(ctx,blockSize,info){
ctx.moveTo(blockSize+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize/2+blockSize*info.offset[1])
ctx.lineTo(blockSize/2+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.closePath()
}
function drawSmallTriBR(ctx,blockSize,info){
ctx.moveTo(blockSize/2+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], blockSize/2+blockSize*info.offset[1])
ctx.closePath()
}
function drawStack(ctx, blockSize, shape,position,rotation){
const halfBlock = blockSize/2
let offsets = []
switch(position){
case 1: //corner
offsets = [[0,0],[3,0],[3,3],[0,3]]
break
case 2: // middle
offsets = [[1,1],[2,1],[2,2],[1,2]]
break
case 3: // edge
offsets = [[[0,1],[1,0]],[[2,0],[3,1]],[[3,2],[2,3]],[[1,3],[0,2]]]
break
}
const shapes = [
function (info) {// top bar (5 mid, o corner)
ctx.rect(([0,2,3].includes(info.rotation)?0:halfBlock)+blockSize*info.offset[0], ([0,1,3].includes(info.rotation)?0:halfBlock)+blockSize*info.offset[1], info.rotIsEven?blockSize: halfBlock, info.rotation%2===0?halfBlock:blockSize)
ctx.fill()
},
function (info) {// lg square (45 corner)
ctx.fillRect(blockSize*info.offset[0], blockSize*info.offset[1],blockSize,blockSize)
},
function(info) {// half sq tri (w4 mid)
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo(([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// middle tri (36 corner)
console.log(info.rotation, info.rotIsEven, info.rotIsZero, info.rotIsOne)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// diamond (3)
ctx.moveTo(halfBlock+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0],halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// corner gem (12)
ctx.moveTo(([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo(([1,3].includes(info.rotation)?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], ([1,3].includes(info.rotation)?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// tritri (15)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize*3/4:blockSize/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize/4:blockSize*3/4))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize/4:blockSize*3/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize*3/4:blockSize/4))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill('evenodd')
ctx.beginPath()
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotation%2!==0?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotation%2===0?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotation==1?blockSize*3/4:blockSize/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize/4:blockSize*3/4))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize/4:blockSize*3/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize*3/4:blockSize/4))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill('evenodd')
},
function(info) {// corner tri (4)
ctx.moveTo(([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// sm sq center (8)
ctx.rect(blockSize/4+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1], halfBlock,halfBlock)
ctx.fill()
},
function(info) {// two small sq half (f)
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// sm sq corner (c)
ctx.rect(([0,3].includes(info.rotation)?0:halfBlock)+blockSize*info.offset[0], (info.rotation<2?0:halfBlock)+blockSize*info.offset[1],halfBlock,halfBlock)
ctx.fill()
},
function(info) {// center tri (e)
ctx.moveTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// edge tri (77)
ctx.moveTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// sm corner tri inv (38)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// sm corner tri (0,yuygjh)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// checkered (2)
ctx.rect(blockSize*info.offset[0], (info.rotIsEven?halfBlock:0)+blockSize*info.offset[1], halfBlock,halfBlock)
ctx.fill()
ctx.beginPath()
ctx.rect(halfBlock+blockSize*info.offset[0], (info.rotIsEven?0:halfBlock)+blockSize*info.offset[1], halfBlock, halfBlock)
ctx.fill()
},
function(info) {// 2 tri (23)
ctx.moveTo(blockSize*info.offset[0], (info.rotIsEven?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?0:blockSize)+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:0)+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], (info.rotIsEven?0:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// long tri (29)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// 2 long tri (2a,2addewsx)
ctx.moveTo(([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// long, short tri (3)
ctx.moveTo(([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// spike (0)
ctx.moveTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// flock (2d)
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// 2 edge tri (1)
ctx.moveTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize/4:blockSize*3/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize*3/4:blockSize/4))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize*3/4:blockSize/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize/4:blockSize*3/4))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// teeth tri (1d)
ctx.moveTo(blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:0)+blockSize*info.offset[0], (info.rotIsEven?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:blockSize/4)+blockSize*info.offset[0], (info.rotIsEven?blockSize/4:halfBlock)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo(blockSize+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?0:blockSize)+blockSize*info.offset[0], (info.rotIsEven?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:blockSize*3/4)+blockSize*info.offset[0], (info.rotIsEven?blockSize*3/4:halfBlock)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// teeth with food (4d)
ctx.moveTo((info.rotIsEven?0:blockSize)+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], (info.rotIsEven?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?0:halfBlock)+blockSize*info.offset[0], (info.rotIsEven?halfBlock:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:0)+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], (info.rotIsEven?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:halfBlock)+blockSize*info.offset[0], (info.rotIsEven?halfBlock:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// sm diamond (10)
ctx.moveTo(halfBlock+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// invert star (31)
ctx.moveTo(blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.closePath()
ctx.fill('evenodd')
},
function(info) {// rewind (7d)
ctx.moveTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// spike cross (4)
ctx.moveTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// invert bar (a)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
ctx.beginPath()
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo(([1,2].includes(info.rotation)?0:blockSize)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// origami (2)
ctx.moveTo(blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.closePath()
ctx.fill('evenodd')
ctx.beginPath()
ctx.moveTo(halfBlock+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// cup (b)
ctx.moveTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize*3/4:blockSize/4))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize*3/4:blockSize/4))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize*3/4:blockSize/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize/4:blockSize*3/4))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize/4:blockSize*3/4))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize/4:blockSize*3/4))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize/4:blockSize*3/4))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize*3/4:blockSize/4))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// squash diamond (8c)
ctx.moveTo((info.rotIsEven?0:halfBlock)+blockSize*info.offset[0], (info.rotIsEven?halfBlock:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:blockSize*3/4)+blockSize*info.offset[0], (info.rotIsEven?blockSize/4:halfBlock)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:halfBlock)+blockSize*info.offset[0], (info.rotIsEven?halfBlock:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:blockSize/4)+blockSize*info.offset[0], (info.rotIsEven?blockSize*3/4:halfBlock)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// tri mirror (1e)
ctx.moveTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.lineTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.closePath()
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.fill()
},
function(info) {// hollow diamond (16)
ctx.moveTo(halfBlock+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo(blockSize+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.moveTo(halfBlock+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.fill('evenodd')
},
function(info) {// rotated 3 tri (1f)
if ([1,2,3].includes(info.rotation)){
drawSmallTriTL(ctx,blockSize,info)
}
if ([0,2,3].includes(info.rotation)){
drawSmallTriTR(ctx,blockSize,info)
}
if ([0,1,2].includes(info.rotation)){
drawSmallTriBL(ctx,blockSize,info)
}
if ([0,1,3].includes(info.rotation)){
drawSmallTriBR(ctx,blockSize,info)
}
ctx.fill()
},
function(info) {// rotated 2 tri (7)
if ([1,2].includes(info.rotation)){
drawSmallTriTL(ctx,blockSize,info)
}
if ([2,3].includes(info.rotation)){
drawSmallTriTR(ctx,blockSize,info)
}
if ([0,1].includes(info.rotation)){
drawSmallTriBL(ctx,blockSize,info)
}
if ([0,3].includes(info.rotation)){
drawSmallTriBR(ctx,blockSize,info)
}
ctx.fill()
},
function(info) {// opposite 2 tri (1)
if (!info.rotIsEven){
drawSmallTriTL(ctx,blockSize,info)
}
if (info.rotIsEven){
drawSmallTriTR(ctx,blockSize,info)
}
if (info.rotIsEven){
drawSmallTriBL(ctx,blockSize,info)
}
if (!info.rotIsEven){
drawSmallTriBR(ctx,blockSize,info)
}
ctx.fill()
},
function(info) {// chevron (7)
ctx.moveTo((!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?blockSize:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// hollow tri (23)
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([1,2].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo(([1,2].includes(info.rotation)?blockSize:0)+blockSize*info.offset[0], (info.rotation<2?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo(([1,3].includes(info.rotation)?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[0], (info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.moveTo(halfBlock+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize/4:blockSize*3/4)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize*3/4:blockSize/4)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize/4:blockSize*3/4)+blockSize*info.offset[0], (info.rotation<2?blockSize/4:blockSize*3/4)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill('evenodd')
},
function(info) {// 2 corner tri(13)
ctx.moveTo((info.rotIsEven?0:blockSize)+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?0:blockSize)+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.closePath()
ctx.moveTo((info.rotIsEven?blockSize:0)+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo(halfBlock+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:0)+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// 2 block corner (3b)
ctx.moveTo((info.rotIsEven?0:blockSize)+blockSize*info.offset[0], blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:blockSize)+blockSize*info.offset[0], (info.rotIsEven?0:halfBlock)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:blockSize*3/4)+blockSize*info.offset[0], (info.rotIsEven?blockSize/4:halfBlock)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?0:halfBlock)+blockSize*info.offset[0], (info.rotIsEven?halfBlock:0)+blockSize*info.offset[1])
ctx.closePath()
ctx.moveTo(halfBlock+blockSize*info.offset[0], blockSize+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:0)+blockSize*info.offset[0], (info.rotIsEven?blockSize*3/4:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:0)+blockSize*info.offset[0], halfBlock+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize:blockSize/4)+blockSize*info.offset[0], (info.rotIsEven?blockSize:halfBlock)+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// 2 diamond (e)
ctx.moveTo((info.rotIsEven?halfBlock:blockSize)+blockSize*info.offset[0], (info.rotIsEven?0:halfBlock)+blockSize*info.offset[1])
ctx.lineTo(blockSize*3/4+blockSize*info.offset[0], (info.rotIsEven?blockSize/4:blockSize*3/4)+blockSize*info.offset[1])
ctx.lineTo(blockSize/4+blockSize*info.offset[0], (info.rotIsEven?blockSize*3/4:blockSize/4)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:0)+blockSize*info.offset[0], (info.rotIsEven?blockSize:halfBlock)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize*3/4:blockSize/4)+blockSize*info.offset[0], blockSize*3/4+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?blockSize/4:blockSize*3/4)+blockSize*info.offset[0], blockSize/4+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
},
function(info) {// 2 stick (3a)
ctx.moveTo((info.rotation<2?0:blockSize)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?blockSize:0)+blockSize*info.offset[1])
ctx.lineTo((info.rotation<2?blockSize:0)+blockSize*info.offset[0], ([0,3].includes(info.rotation)?0:blockSize)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?blockSize:0))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?0:blockSize))+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize/4:blockSize*3/4)+blockSize*info.offset[0], (info.rotation<2?blockSize/4:blockSize*3/4)+blockSize*info.offset[1])
ctx.lineTo(([0,3].includes(info.rotation)?blockSize*3/4:blockSize/4)+blockSize*info.offset[0], (info.rotation<2?blockSize*3/4:blockSize/4)+blockSize*info.offset[1])
ctx.lineTo((info.rotIsEven?halfBlock:(info.rotIsOne?0:blockSize))+blockSize*info.offset[0], (!info.rotIsEven?halfBlock:(info.rotIsZero?blockSize:0))+blockSize*info.offset[1])
ctx.closePath()
ctx.fill()
}
]
let data = {
rotation: rotation,
offset: [],
rotIsEven: false
}
if (shape < shapes.length) {
offsets.forEach(offset => {
// for readability
data.rotIsEven = data.rotation%2===0
data.rotIsZero = data.rotation === 0
data.rotIsOne = data.rotation === 1
if(Array.isArray(offset[0])) {
data.offset = offset[0]
ctx.beginPath()
shapes[shape](data)
data.offset = offset[1]
ctx.beginPath()
shapes[shape](data)
} else {
data.offset = offset
ctx.beginPath()
shapes[shape](data)
}
data.rotation+=1
data.rotation%=4
})
}
}
function generateStack(){
// const hash = document.getElementById('seed').value.murmurHash()
const hash = document.getElementById('seed').value.cyrb53Hash()
const color = `#${hash.substring(1,4)}`
const invert = parseInt(hash.substring(4,5))<8
const c = document.getElementById('identicon')
const ctx = c.getContext('2d')
ctx.clearRect(0, 0, c.width,c.height)
const blockSize = Math.floor(c.width/4)
ctx.fillStyle = color
if(invert){
ctx.beginPath()
ctx.rect(0,0,c.width,c.height)
ctx.fill()
ctx.fillStyle='white'
}
let shape,rotation
rotation = Math.floor(parseInt(hash.substring(3,4),16)/4)
shape = Math.floor(parseInt(hash.substring(4,6),16)*45/256)
drawStack(ctx,blockSize,shape,1,rotation)
rotation = Math.floor(parseInt(hash.substring(4,5),16)/4)
shape = Math.floor(parseInt(hash.substring(5,6)+hash.substring(0,1),16)*45/256)
drawStack(ctx,blockSize,shape,2,rotation)
rotation = Math.floor(parseInt(hash.substring(6,7),16)/4)
shape = Math.floor(parseInt(hash.substring(6,8),16)*45/256)
drawStack(ctx, blockSize, shape, 3, rotation)
}
function generateIdenticon(){
const identiconSize = document.getElementById('size').value
document.getElementById('identicon').height = identiconSize
document.getElementById('identicon').width = identiconSize
generateStack()
}
generateIdenticon()
// https://stackoverflow.com/a/47593316/3462319
// for random numbers
function mulberry32(a) {
return function(){
let t = a += 0x6d2b79f5
t = Math.imul(t ^ t >>> 15, t | 1)
t ^= t + Math.imul(t ^ t >>> 7, t | 61)
return ((t ^ t >>> 14) >>> 0) / 4294967296
}
}
// https://stackoverflow.com/a/47593316/3462319
// length: 8
Object.defineProperty(String.prototype, 'cyrb53Hash', {
value: function () {
let seed = 0
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for(let i = 0, ch; i < this.length; i++) {
ch = this.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, '0');
}
})
// https://github.com/garycourt/murmurhash-js/blob/master/murmurhash3_gc.js
// length: 14
Object.defineProperty(String.prototype, 'murmurHash', {
value: function() {
let seed = 0
var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i;
remainder = this.length & 3; // key.length % 4
bytes = this.length - remainder;
h1 = seed;
c1 = 0xcc9e2d51;
c2 = 0x1b873593;
i = 0;
while (i < bytes) {
k1 =
((this.charCodeAt(i) & 0xff)) |
((this.charCodeAt(++i) & 0xff) << 8) |
((this.charCodeAt(++i) & 0xff) << 16) |
((this.charCodeAt(++i) & 0xff) << 24);
++i;
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
}
k1 = 0;
switch (remainder) {
case 3: k1 ^= (this.charCodeAt(i + 2) & 0xff) << 16;
case 2: k1 ^= (this.charCodeAt(i + 1) & 0xff) << 8;
case 1: k1 ^= (this.charCodeAt(i) & 0xff);
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
}
h1 ^= this.length;
h1 ^= h1 >>> 16;
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
h1 ^= h1 >>> 16;
return (h1 >>> 0).toString(16).padStart(8, '0');
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment