0x77b35947…cc7dsent to0xc3e1f0dd…9515·#25,613,204·view on Etherscan
()
pg.translate(-hollowX, hollowY)
pg.beginShape()
pg.vertex(-hollowWidth * 0.35, -hollowHeight * 0.5)
pg.vertex(hollowWidth * 0.35, -hollowHeight * 0.5)
pg.bezierVertex(
hollowWidth * 0.45, -hollowHeight * 0.2,
hollowWidth * 0.5, hollowHeight * 0.2,
hollowWidth * 0.4, hollowHeight * 0.5
)
pg.vertex(-hollowWidth * 0.4, hollowHeight * 0.5)
pg.bezierVertex(
-hollowWidth * 0.5, hollowHeight * 0.2,
-hollowWidth * 0.45, -hollowHeight * 0.2,
-hollowWidth * 0.35, -hollowHeight * 0.5
)
pg.endShape(CLOSE)
pg.pop()
} else {
pg.push()
pg.translate(hollowX, hollowY)
pg.beginShape()
pg.vertex(-hollowWidth * 0.35, -hollowHeight * 0.5)
pg.vertex(hollowWidth * 0.35, -hollowHeight * 0.5)
pg.bezierVertex(
hollowWidth * 0.45, -hollowHeight * 0.2,
hollowWidth * 0.5, hollowHeight * 0.2,
hollowWidth * 0.4, hollowHeight * 0.5
)
pg.vertex(-hollowWidth * 0.4, hollowHeight * 0.5)
pg.bezierVertex(
-hollowWidth * 0.5, hollowHeight * 0.2,
-hollowWidth * 0.45, -hollowHeight * 0.2,
-hollowWidth * 0.35, -hollowHeight * 0.5
)
pg.endShape(CLOSE)
pg.pop()
}
} else {
pg.push()
pg.translate(-hollowX, hollowY)
pg.beginShape()
pg.vertex(-hollowWidth * 0.35, -hollowHeight * 0.5)
pg.vertex(hollowWidth * 0.35, -hollowHeight * 0.5)
pg.bezierVertex(
hollowWidth * 0.45, -hollowHeight * 0.2,
hollowWidth * 0.5, hollowHeight * 0.2,
hollowWidth * 0.4, hollowHeight * 0.5
)
pg.vertex(-hollowWidth * 0.4, hollowHeight * 0.5)
pg.bezierVertex(
-hollowWidth * 0.5, hollowHeight * 0.2,
-hollowWidth * 0.45, -hollowHeight * 0.2,
-hollowWidth * 0.35, -hollowHeight * 0.5
)
pg.endShape(CLOSE)
pg.pop()
pg.push()
pg.translate(hollowX, hollowY)
pg.beginShape()
pg.vertex(-hollowWidth * 0.35, -hollowHeight * 0.5)
pg.vertex(hollowWidth * 0.35, -hollowHeight * 0.5)
pg.bezierVertex(
hollowWidth * 0.45, -hollowHeight * 0.2,
hollowWidth * 0.5, hollowHeight * 0.2,
hollowWidth * 0.4, hollowHeight * 0.5
)
pg.vertex(-hollowWidth * 0.4, hollowHeight * 0.5)
pg.bezierVertex(
-hollowWidth * 0.5, hollowHeight * 0.2,
-hollowWidth * 0.45, -hollowHeight * 0.2,
-hollowWidth * 0.35, -hollowHeight * 0.5
)
pg.endShape(CLOSE)
pg.pop()
}
}
}
function drawOrnaments(pg, m) {
if (m.ornament === "Bare") return
pg.push()
pg.noStroke()
pg.fill(255)
if (m.ornament === "Links") {
const earringSize = m.w * 0.08
const earringX = m.w * 0.40
const earringY = m.h * 0.15
pg.circle(-earringX, earringY, earringSize)
pg.circle(earringX, earringY, earringSize)
const smallSize1 = earringSize * 0.65
const smallSize2 = earringSize * 0.5
const smallSize3 = earringSize * 0.35
const dropSpacing = earringSize * 0.55
pg.circle(-earringX, earringY + dropSpacing, smallSize1)
pg.circle(-earringX, earringY + dropSpacing * 1.8, smallSize2)
pg.circle(-earringX, earringY + dropSpacing * 2.5, smallSize3)
pg.circle(earringX, earringY + dropSpacing, smallSize1)
pg.circle(earringX, earringY + dropSpacing * 1.8, smallSize2)
pg.circle(earringX, earringY + dropSpacing * 2.5, smallSize3)
} else if (m.ornament === "Amp") {
pg.rectMode(CENTER)
const markWidth = m.w * 0.03
const markHeight = m.h * 0.15
const markX = m.w * 0.38
const markY = 0
pg.rect(-markX, markY, markWidth, markHeight)
pg.rect(markX, markY, markWidth, markHeight)
} else if (m.ornament === "Crest") {
const spikeCount = 5
const baseWidth = m.w * 0.1
const spacing = m.w * 0.15
const maskTopY = -m.h * 0.4
const heights = [
m.h * 0.16,
m.h * 0.22,
m.h * 0.26,
m.h * 0.22,
m.h * 0.16
]
const verticalOffsets = [50, 0, 0, 0, 50]
pg.stroke(0, 0, 0)
pg.strokeWeight(m.w * 0.015)
for (let i = 0; i < spikeCount; i++) {
const x = (i - (spikeCount - 1) / 2) * spacing
const spikeHeight = heights[i]
const baseY = maskTopY + verticalOffsets[i]
const spikeY = baseY - spikeHeight
const isOuter = (i === 0 || i === spikeCount - 1)
const angleOffset = isOuter ? (i === 0 ? baseWidth * 0.3 : -baseWidth * 0.3) : 0
pg.triangle(
x, spikeY,
x - baseWidth / 2 + (i === 0 ? angleOffset : 0), baseY,
x + baseWidth / 2 + (i === spikeCount - 1 ? angleOffset : 0), baseY
)
const connectorHeight = m.h * 0.10
const topWidth = baseWidth * 0.9
const bottomWidth = baseWidth * 0.6
pg.beginShape()
pg.vertex(x - baseWidth / 2 + (i === 0 ? angleOffset : 0), baseY)
pg.vertex(x + baseWidth / 2 + (i === spikeCount - 1 ? angleOffset : 0), baseY)
pg.vertex(x + bottomWidth / 2, baseY + connectorHeight)
pg.vertex(x - bottomWidth / 2, baseY + connectorHeight)
pg.endShape(CLOSE)
}
pg.noStroke()
} else if (m.ornament === "Bestia") {
pg.fill(0, 0, 10)
const hornBaseY = -m.h * 0.25
const hornSpacing = m.w * 0.25
const hornHeight = m.h * 0.35
const hornWidth = m.w * 0.12
for (let side of [-1, 1]) {
pg.push()
pg.translate(hornSpacing * side, hornBaseY)
pg.scale(side, 1)
pg.rotate(-0.25)
pg.beginShape()
pg.vertex(hornWidth * 0.3, 0)
pg.vertex(-hornWidth * 0.3, 0)
pg.bezierVertex(
-hornWidth * 0.5, -hornHeight * 0.15,
-hornWidth * 0.7, -hornHeight * 0.25,
-hornWidth * 0.6, -hornHeight * 0.35
)
pg.vertex(-hornWidth * 0.9, -hornHeight * 0.32)
pg.vertex(-hornWidth * 0.55, -hornHeight * 0.45)
pg.bezierVertex(
-hornWidth * 0.65, -hornHeight * 0.55,
-hornWidth * 0.6, -hornHeight * 0.65,
-hornWidth * 0.45, -hornHeight * 0.72
)
pg.vertex(-hornWidth * 0.75, -hornHeight * 0.70)
pg.vertex(-hornWidth * 0.35, -hornHeight * 0.82)
pg.bezierVertex(
-hornWidth * 0.3, -hornHeight * 0.92,
-hornWidth * 0.1, -hornHeight * 0.98,
hornWidth * 0.05, -hornHeight
)
pg.bezierVertex(
hornWidth * 0.15, -hornHeight * 0.75,
hornWidth * 0.2, -hornHeight * 0.45,
hornWidth * 0.3, 0
)
pg.endShape(CLOSE)
pg.pop()
}
}
else if (m.ornament === "Root") {
const tuskWidth = m.w * 0.15
const tuskHeight = m.h * 0.4
const tuskSpacing = m.w * 0.20
const tuskY = m.h * 0.22
pg.push()
pg.translate(-tuskSpacing, tuskY)
pg.beginShape()
pg.vertex(-tuskWidth * 0.35, 0)
pg.bezierVertex(
-tuskWidth * 0.25, tuskHeight * 0.6,
-tuskWidth * 0.05, tuskHeight * 0.8,
0, tuskHeight
)
pg.bezierVertex(
tuskWidth * 0.35, tuskHeight * 0.8,
tuskWidth * 0.5, tuskHeight * 0.6,
tuskWidth * 0.05, 0
)
pg.endShape(CLOSE)
pg.pop()
pg.push()
pg.translate(tuskSpacing, tuskY)
pg.beginShape()
pg.vertex(-tuskWidth * 0.35, 0)
pg.bezierVertex(
-tuskWidth * 0.25, tuskHeight * 0.6,
-tuskWidth * 0.15, tuskHeight * 0.8,
0, tuskHeight
)
pg.bezierVertex(
tuskWidth * 0.15, tuskHeight * 0.8,
tuskWidth * 0.25, tuskHeight * 0.6,
tuskWidth * 0.35, 0
)
pg.endShape(CLOSE)
pg.pop()
}
else if (m.ornament === "Gauge") {
const gaugeSize = m.w * 0.12
const gaugeX = m.w * 0.42
const gaugeY = m.h * 0.12
pg.circle(-gaugeX, gaugeY, gaugeSize)
pg.circle(gaugeX, gaugeY, gaugeSize)
pg.push()
pg.erase()
const holeSize = gaugeSize * 0.5
pg.circle(-gaugeX, gaugeY, holeSize)
pg.circle(gaugeX, gaugeY, holeSize)
pg.noErase()
pg.pop()
}
else if (m.ornament === "Thorns") {
const spikeCount = 3
const spikeWidth = m.w * 0.05
const spikeHeight = m.h * 0.08
const earX = m.w * 0.42
const startY = m.h * 0.05
const spacing = m.h * 0.1
for (let side of [-1, 1]) {
for (let i = 0; i < spikeCount; i++) {
const x = earX * side
const y = startY + i * spacing
pg.push()
pg.translate(x, y)
pg.triangle(
0, -spikeHeight * 0.5,
-spikeWidth * 0.5 * side, spikeHeight * 0.5,
spikeWidth * 0.5 * side, spikeHeight * 0.5
)
pg.pop()
}
}
}
else if (m.ornament === "Tandem") {
const earringSize = m.w * 0.06
const earringX = m.w * 0.44
const earringY1 = m.h * 0.10
const earringY2 = m.h * 0.20
pg.circle(-earringX, earringY1, earringSize)
pg.circle(earringX, earringY1, earringSize)
pg.circle(-earringX, earringY2, earringSize * 0.8)
pg.circle(earringX, earringY2, earringSize * 0.8)
}
else if (m.ornament === "Spike") {
const spikeWidth = m.w * 0.12
const spikeHeight = m.h * 0.22
const chinY = m.h * 0.32
pg.push()
pg.translate(0, chinY)
pg.triangle(
-spikeWidth / 2, 0,
spikeWidth / 2, 0,
0, spikeHeight
)
pg.pop()
}
pg.pop()
}
const vertSrc = `
attribute vec3 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
vTexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
vec4 positionVec4 = vec4(aPosition, 1.0);
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
gl_Position = positionVec4;
}
`;
const fragDigitalStitchOptimized = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 vTexCoord;
uniform float u_time;
uniform vec2 u_resolution;
uniform sampler2D u_texture;
uniform float u_gridX;
uniform bool u_forceMonochrome;
uniform bool u_applyNoise;
uniform float u_symbolOffset;
uniform float u_vocabSize;
uniform float u_symbolSize;
uniform bool u_bwMode;
float hash21(vec2 p){
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
// Simple 2D Perlin-style noise function
float noise2D(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
// Smooth interpolation
f = f * f * (3.0 - 2.0 * f);
// Four corners
float a = hash21(i);
float b = hash21(i + vec2(1.0, 0.0));
float c = hash21(i + vec2(0.0, 1.0));
float d = hash21(i + vec2(1.0, 1.0));
// Bilinear interpolation
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
// Convert RGB to HSB
vec3 rgb2hsb(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
// Convert HSB to RGB
vec3 hsb2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
float getCharPattern(float shapeId, vec2 cellUV, float brightness){
vec2 p = (cellUV - 0.5) / u_symbolSize;
int id = int(shapeId);
if (id == 0) { // Horizontal bar
return step(abs(p.y), 0.28);
}
if (id == 1) { // Vertical bar
return step(abs(p.x), 0.28);
}
if (id == 2) { // Filled circle
return step(length(p), 0.48);
}
if (id == 3) { // Square
return step(max(abs(p.x), abs(p.y)), 0.45);
}
if (id == 4) { // Diamond
return step(abs(p.x) + abs(p.y), 0.65);
}
if (id == 5) { // Thick cross
return max(step(abs(p.y), 0.22), step(abs(p.x), 0.22));
}
if (id == 6) { // Ring
float ring = length(p); return step(ring, 0.48) * (1.0 - step(ring, 0.12));
}
if (id == 7) { // Checkerboard
return mod(floor(cellUV.x*2.0) + floor(cellUV.y*2.0), 2.0);
}
if (id == 8) { // Small circle
return step(length(p), 0.38);
}
if (id == 9) { // X
return step(abs(abs(p.x) - abs(p.y)), 0.18);
}
if (id == 10) { // Rounded quad
vec2 corner = abs(p) - vec2(0.22); return step(length(corner), 0.22);
}
if (id == 11) { // Thin cross
return max(step(abs(p.y), 0.18), step(abs(p.x), 0.18));
}
if (id == 12) { // Heart
float lobeL = step(length(p - vec2(-0.15, 0.13)), 0.20);
float lobeR = step(length(p - vec2(0.15, 0.13)), 0.20);
float tri = step(abs(p.x) - (p.y + 0.35) * 0.73, 0.0) * step(-0.35, p.y) * step(p.y, 0.13);
return min(lobeL + lobeR + tri, 1.0);
}
if (id == 13) { // 5-petal flower
float a = atan(p.y, p.x); float r = length(p); return step(r, abs(cos(a * 2.5)) * 0.15 + 0.25);
}
if (id == 14) { // Triangle up
return step(abs(p.x) * 0.577 + p.y, 0.25) * step(-p.y, 0.35);
}
if (id == 15) { // Triangle down
return step(abs(p.x) * 0.577 - p.y, 0.25) * step(p.y, 0.35);
}
if (id == 16) { // Arrow up
float shaft = step(abs(p.x), 0.1) * step(p.y, 0.1) * step(-0.3, p.y);
float head = step(abs(p.x) - (0.3 - p.y), 0.0) * step(0.1, p.y) * step(p.y, 0.3);
return max(shaft, head);
}
if (id == 17) { // Arrow right
float shaft = step(abs(p.y), 0.1) * step(p.x, 0.1) * step(-0.3, p.x);
float head = step(abs(p.y) - (0.3 - p.x), 0.0) * step(0.1, p.x) * step(p.x, 0.3);
return max(shaft, head);
}
if (id == 18) { // Hexagon
vec2 q = abs(p); return step(q.x * 0.866 + q.y * 0.5, 0.4);
}
if (id == 19) { // Dot
return step(length(p), 0.12);
}
if (id == 20) { // Two circles
return max(step(length(p - vec2(-0.15, 0.0)), 0.15), step(length(p - vec2(0.15, 0.0)), 0.15));
}
if (id == 21) { // Grid
return max(step(0.7, fract(cellUV.x * 3.0)), step(0.7, fract(cellUV.y * 3.0)));
}
if (id == 22) { // Diagonal /
return step(0.7, fract((cellUV.x + cellUV.y) * 5.0));
}
if (id == 23) { // Diagonal \\
return step(0.7, fract((cellUV.x - cellUV.y) * 5.0));
}
if (id == 24) { // Cross with dots
float plus = max(step(abs(p.y), 0.12), step(abs(p.x), 0.12));
float dots = step(length(p - vec2(0.3, 0.3)), 0.08) + step(length(p - vec2(-0.3, 0.3)), 0.08) +
step(length(p - vec2(0.3, -0.3)), 0.08) + step(length(p - vec2(-0.3, -0.3)), 0.08);
return min(plus + dots, 1.0);
}
if (id == 25) { // Four corner dots
return max(max(step(length(p - vec2(-0.2, -0.2)), 0.15), step(length(p - vec2(0.2, -0.2)), 0.15)),
max(step(length(p - vec2(-0.2, 0.2)), 0.15), step(length(p - vec2(0.2, 0.2)), 0.15)));
}
if (id == 26) { // Quincunx
float center = step(length(p), 0.15);
float pts = step(length(p - vec2(0.0, 0.25)), 0.12) + step(length(p - vec2(0.25, 0.0)), 0.12) +
step(length(p - vec2(0.0, -0.25)), 0.12) + step(length(p - vec2(-0.25, 0.0)), 0.12);
return min(center + pts, 1.0);
}
if (id == 27) { // Octagon
vec2 q = abs(p); return step(max(q.x * 0.707 + q.y * 0.707, max(q.x, q.y)), 0.4);
}
if (id == 28) { // Vertical stripes
return step(0.6, fract(cellUV.x * 4.0));
}
if (id == 29) { // Horizontal stripes
return step(0.6, fract(cellUV.y * 4.0));
}
if (id == 30) { // Concentric rings
float r = length(p); return step(0.7, fract(r * 10.0));
}
if (id == 31) { // Crescent
float outer = step(length(p), 0.38); float inner = step(length(p - vec2(0.18, 0.0)), 0.32); return outer * (1.0 - inner);
}
if (id == 32) { // 5-pointed star
float a = atan(p.y, p.x) + 1.5708; float r = length(p);
return step(r, cos(floor(0.5 + a / 1.2566) * 1.2566 - a) * 0.45);
}
if (id == 33) { // Scouts [REPLACED]
float a = atan(p.y, p.x); float r = length(p);
float star = step(r, abs(cos(a * 4.0)) * 0.32 + 0.05);
float diamond = step(abs(p.x) + abs(p.y), 0.10);
return max(star, diamond);
}
if (id == 34) { // Bowtie [REPLACED]
return step(abs(p.x) * 0.5 - abs(p.y), 0.0) * step(abs(p.x), 0.4) * step(abs(p.y), 0.35);
}
if (id == 35) { // Rook
float body = step(abs(p.x), 0.22) * step(abs(p.y), 0.32);
float notch1 = step(abs(p.x + 0.12), 0.04) * step(p.y, 0.35) * step(0.22, p.y);
float notch2 = step(abs(p.x - 0.12), 0.04) * step(p.y, 0.35) * step(0.22, p.y);
return body * (1.0 - notch1) * (1.0 - notch2);
}
if (id == 36) { // Circuit node [REPLACED]
float c = step(length(p), 0.1);
float l1 = step(abs(p.y), 0.03) * step(abs(p.x), 0.4);
float l2 = step(abs(p.x), 0.03) * step(abs(p.y), 0.4);
return min(c + l1 + l2, 1.0);
}
if (id == 37) { // Resistor [REPLACED]
float l1 = step(abs(p.y), 0.03) * step(abs(p.x + 0.3), 0.1);
float l2 = step(abs(p.y), 0.03) * step(abs(p.x - 0.3), 0.1);
float box = step(abs(p.y), 0.1) * step(abs(p.x), 0.2);
return min(l1 + l2 + box, 1.0);
}
if (id == 38) { // Trace L [REPLACED]
float h = step(abs(p.y), 0.03) * step(p.x, 0.4) * step(-0.03, p.x);
float v = step(abs(p.x), 0.03) * step(p.y, 0.03) * step(-0.4, p.y);
return max(h, v);
}
if (id == 39) { // Lightning
vec2 q = p * 2.5;
float b1 = step(abs(q.x - 0.2), 0.18) * step(q.y, 0.8) * step(0.0, q.y);
float b2 = step(abs(q.x + 0.1), 0.25) * step(q.y, 0.15) * step(-0.15, q.y);
float b3 = step(abs(q.x + 0.2), 0.18) * step(q.y, -0.0) * step(-0.8, q.y);
return min(b1 + b2 + b3, 1.0);
}
if (id == 40) { // Hourglass
float w = min(abs(p.y) * 0.9 + 0.03, 0.28); return step(abs(p.x), w) * step(abs(p.y), 0.38);
}
if (id == 41) { // Infinity
float l1 = length(p - vec2(-0.18, 0.0)); float l2 = length(p - vec2(0.18, 0.0));
return max(step(l1, 0.2) * (1.0 - step(l1, 0.08)), step(l2, 0.2) * (1.0 - step(l2, 0.08)));
}
if (id == 42) { // Wave crest [REPLACED]
return step(abs(p.y - sin(p.x * 8.0) * 0.1), 0.06) * step(abs(p.x), 0.4);
}
if (id == 43) { // Eye
float almond = step(abs(p.x) * 0.5 + abs(p.y), 0.22); return max(almond * 0.7, step(length(p), 0.08));
}
if (id == 44) { // Skull
float head = step(length(p - vec2(0.0, 0.08)), 0.26);
float jaw = step(abs(p.x), 0.14) * step(abs(p.y + 0.2), 0.1);
float eyeL = 1.0 - step(length(p - vec2(-0.09, 0.12)), 0.07);
float eyeR = 1.0 - step(length(p - vec2(0.09, 0.12)), 0.07);
return min(head + jaw, 1.0) * eyeL * eyeR;
}
if (id == 45) { // Spade
float lobeL = step(length(p - vec2(-0.15, -0.05)), 0.20);
float lobeR = step(length(p - vec2(0.15, -0.05)), 0.20);
float tri = step(abs(p.x) - (0.32 - p.y) * 0.95, 0.0) * step(-0.05, p.y) * step(p.y, 0.32);
float stem = step(abs(p.x), 0.07) * step(p.y, -0.22) * step(-0.34, p.y);
return min(lobeL + lobeR + tri + stem, 1.0);
}
if (id == 46) { // Club
float c1 = step(length(p - vec2(0.0, 0.18)), 0.16);
float c2 = step(length(p - vec2(-0.16, -0.05)), 0.16);
float c3 = step(length(p - vec2(0.16, -0.05)), 0.16);
float center = step(length(p - vec2(0.0, 0.03)), 0.10);
float stem = step(abs(p.x), 0.07) * step(p.y, -0.05) * step(-0.34, p.y);
return min(c1 + c2 + c3 + center + stem, 1.0);
}
if (id == 47) { // Trefoil
float c1 = step(length(p - vec2(0.0, 0.15)), 0.14);
float c2 = step(length(p - vec2(-0.13, -0.05)), 0.14);
float c3 = step(length(p - vec2(0.13, -0.05)), 0.14);
return min(c1 + c2 + c3, 1.0);
}
if (id == 48) { // Peace sign
float r = length(p);
float ring = step(r, 0.35) * (1.0 - step(r, 0.27));
float inside = step(r, 0.30);
float vert = step(abs(p.x), 0.04) * inside;
float legL = step(abs(p.x + p.y * 0.65), 0.04) * step(p.y, 0.0) * inside;
float legR = step(abs(p.x - p.y * 0.65), 0.04) * step(p.y, 0.0) * inside;
return min(ring + vert + legL + legR, 1.0);
}
if (id == 49) { // Warriors [REPLACED]
float armH = step(abs(p.y), max(0.0, 0.12 - abs(p.x) * 0.32)) * step(abs(p.x), 0.38);
float armV = step(abs(p.x), max(0.0, 0.12 - abs(p.y) * 0.32)) * step(abs(p.y), 0.38);
float diamond = step(abs(p.x) + abs(p.y), 0.13);
return max(max(armH, armV), diamond);
}
if (id == 50) { // Nobles [REPLACED]
float d1 = step(abs(p.x) + abs(p.y), 0.12);
float d2 = step(abs(p.x) + abs(p.y - 0.26), 0.10);
float d3 = step(abs(p.x) + abs(p.y + 0.26), 0.10);
float d4 = step(abs(p.x - 0.26) + abs(p.y), 0.10);
float d5 = step(abs(p.x + 0.26) + abs(p.y), 0.10);
return min(d1 + d2 + d3 + d4 + d5, 1.0);
}
if (id == 51) { // Conflux [REPLACED]
float c = 0.866; float s = 0.5;
vec2 q1 = vec2((p.x - 0.10) * c + (p.y - 0.08) * s, -(p.x - 0.10) * s + (p.y - 0.08) * c);
vec2 q2 = vec2((p.x + 0.10) * c + (p.y + 0.08) * s, -(p.x + 0.10) * s + (p.y + 0.08) * c);
float r1 = step(max(abs(q1.x), abs(q1.y)), 0.14);
float r2 = step(max(abs(q2.x), abs(q2.y)), 0.14);
return min(r1 + r2, 1.0);
}
return step(max(abs(p.x), abs(p.y)), 0.2); // fallback
}
void main(){
vec2 uv = vTexCoord;
float GRID_X = max(20.0, u_gridX);
float aspect = u_resolution.x / u_resolution.y;
vec2 gridCount = vec2(GRID_X, GRID_X / aspect);
vec2 gridUV = uv * gridCount;
vec2 cellID = floor(gridUV);
vec2 cellUV = fract(gridUV);
vec2 cellCenter = (cellID + 0.5) / gridCount;
vec4 cellColor = texture2D(u_texture, cellCenter);
if (cellColor.a < 0.01) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
// Enhanced brightness calculation with better contrast
float cellBri = dot(cellColor.rgb, vec3(0.299, 0.587, 0.114));
cellBri = pow(cellBri, 0.8); // Gamma adjustment for better distribution
// Conditionally apply Perlin noise texture
// Slow-moving organic texture across the surface
if (u_applyNoise) {
vec2 noiseCoord = cellCenter * u_resolution * 0.003 + u_time * 0.05;
float noiseVal = noise2D(noiseCoord);
// Apply subtle noise variation to brightness (affects pattern selection)
cellBri = cellBri + (noiseVal - 0.5) * 0.12;
}
cellBri = clamp(cellBri, 0.0, 1.0);
// Symbol selection — brightness picks an index within the active vocabulary,
// which maps to a contiguous block of slots starting at u_symbolOffset.
// u_vocabSize controls how many distinct symbols a piece uses (1, 2, 5, 10, or 52).
float TYPES = 52.0;
float vocab = max(u_vocabSize, 1.0);
float idx = floor(cellBri * vocab);
idx = min(idx, vocab - 1.0); // clamp top edge (cellBri = 1.0 case)
float shapeId = mod(u_symbolOffset + idx, TYPES);
// Get the pattern mask
float mask = getCharPattern(shapeId, cellUV, cellBri);
mask = clamp(mask, 0.0, 1.0);
// Convert cell color to HSB
vec3 hsbColor = rgb2hsb(cellColor.rgb);
// Check if this is a grayscale/desaturated color (like the form)
float isGrayscale = step(hsbColor.y, 0.15); // Saturation threshold — lower = more cells use chromatic path
// OVERRIDE: Force monochrome mode if requested
if (u_forceMonochrome) {
isGrayscale = 1.0;
}
vec3 bgColor, symbolColor;
if (isGrayscale > 0.5 || u_forceMonochrome) {
// MONOCHROMATIC MODE - use single hue with varying sat/brightness
float baseHue = hsbColor.x;
if (u_forceMonochrome) {
// For monotone palettes: create monochromatic scheme
bgColor = hsb2rgb(vec3(baseHue, hsbColor.y * 0.3, hsbColor.z * 1.2));
symbolColor = hsb2rgb(vec3(baseHue, hsbColor.y * 0.9, hsbColor.z * 0.7));
} else {
// For form/grayscale elements: true grayscale
bgColor = vec3(hsbColor.z * 1.15);
symbolColor = vec3(hsbColor.z * 0.6);
}
} else {
// COMPLEMENTARY COLOR MODE (for vibrant mask fills)
// Create background color - slightly adjusted version of original
bgColor = hsb2rgb(vec3(hsbColor.x, hsbColor.y * 0.8, hsbColor.z * 1.2));
// Create symbol color - complementary hue
float symbolHue = mod(hsbColor.x + 0.5, 1.0);
// Vary symbol color based on cell position for more variety
float cellHash = hash21(cellID);
float hueVariation = (cellHash - 0.5) * 0.2;
symbolHue = mod(symbolHue + hueVariation, 1.0);
// Choose saturation and brightness for symbol
float symbolSat = mix(0.7, 1.0, cellHash);
float symbolBri = cellBri > 0.5 ? 0.3 : 0.9;
symbolColor = hsb2rgb(vec3(symbolHue, symbolSat, symbolBri));
}
// Mix background and symbol colors
vec3 finalColor = mix(bgColor, symbolColor, mask);
// Ink mode — pure black & white, hard luminance threshold (Etched palette).
if (u_bwMode) {
float lum = dot(finalColor, vec3(0.299, 0.587, 0.114));
finalColor = lum > 0.5 ? vec3(1.0) : vec3(0.0);
}
gl_FragColor = vec4(finalColor, cellColor.a);
}
`
function logTraits() {
if (!DEBUG) return
console.log('%c╔═══════════════════════════════════════════════════════════╗', 'color: #FF00FF;')
console.log('%c║ SATARI TRAITS ║', 'color: #FF00FF; font-weight: bold; font-size: 16px;')
console.log('%c╚═══════════════════════════════════════════════════════════╝', 'color: #FF00FF;')
console.log(`%c SEED: ${CURRENT_SEED}`, 'color: #FFD700; font-weight: bold; font-size: 14px;')
console.log('')
console.log('%c ─── PRIMARY ───', 'color: #00FFFF; font-weight: bold;')
console.log(`%c Form: %c${TRAITS.displayedArchetype}`, 'color: #888;', 'color: #FFF; font-weight: bold;')
console.log(`%c House: %c${TRAITS.house}`, 'color: #888;', 'color: #FFF; font-weight: bold;')
console.log(`%c Class: %c${TRAITS.role}`, 'color: #888;', 'color: #FFF; font-weight: bold;')
console.log(`%c Palette: %c${TRAITS.paletteName}${TRAITS._basePalette ? ' (over ' + TRAITS._basePalette + ')' : ''}`, 'color: #888;', 'color: #FFF; font-weight: bold;')
console.log(`%c Realm: %c${TRAITS.realm}`, 'color: #888;', 'color: #FFF; font-weight: bold;')
console.log(`%c Aura: %c${TRAITS.aura}`, 'color: #888;', 'color: #FFF; font-weight: bold;')
console.log('')
console.log('%c ─── IDENTITY ───', 'color: #00FFFF; font-weight: bold;')
console.log(`%c Doctrine: %c${TRAITS.doctrine}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Spacecraft: %c${TRAITS.spacecraft}`, 'color: #888;', 'color: #FFF;')
console.log('')
console.log('%c ─── FACE ───', 'color: #00FFFF; font-weight: bold;')
console.log(`%c Sight: %c${TRAITS.sight}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Iris: %c${TRAITS.iris}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Voice: %c${TRAITS.voice}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Scent: %c${TRAITS.scent}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Hollows: %c${TRAITS.hollows}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Ornament: %c${TRAITS.ornament}`, 'color: #888;', 'color: #FFF;')
console.log('')
console.log('%c ─── ANIMATION ───', 'color: #00FFFF; font-weight: bold;')
console.log(`%c Fill: %c${TRAITS.fillVariant}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Blend: %c${TRAITS.blendSchema}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Origin: %c${TRAITS.originTopology}`, 'color: #888;', 'color: #FFF;')
console.log(`%c Spawn: %c${TRAITS.spawnRhythm}`, 'color: #888;', 'color: #FFF;')
console.log('')
console.log('%c ─── CONTROLS ───', 'color: #888;')
console.log('%c N = Restart | S = Save PNG | Space = Freeze | Esc = Reset Modes', 'color: #666; font-size: 11px;')
console.log('%c═══════════════════════════════════════════════════════════════', 'color: #FF00FF;')
}
function resetAndRegenerate() {
fillAnimations = []
orbitingElements = []
masks = []
maskBoundsCache = []
globalBounds = null
eyesFullyOn = false
regenStartFrame = frameCount // Track when this regeneration started
nextSpawnFrame = 0
_breathStartMillis = null
if (animationPool) {
animationPool.clear()
}
if (fillPersistentLayer) {
fillPersistentLayer.clear()
}
applySeed()
TRAITS = buildTraits()
bwMode = TRAITS.paletteName === "Etched"
PARAMS = resolveParams(TRAITS)
logTraits()
layoutMasks()
calculateMaskBounds()
isDirty = true
drawStaticLayers()
initTopology()
initOrbitingElements()
}
// Exposed for external tools (e.g. the rater): swap to a new seed without
// reloading the page. Keeps the same shader/canvas/buffers — only re-rolls
// traits and rebuilds layout. Much faster than an iframe reload.
window.regenerateWithSeed = function (newSeed) {
CURRENT_SEED = newSeed >>> 0
resetAndRegenerate()
}
function getTierForWidth(displayWidth) {
let tier = QUALITY_TIERS[0]
for (const t of QUALITY_TIERS) {
if (displayWidth >= t.minWidth) tier = t
}
return tier
}
function getDisplayWidth() {
const canvas = document.querySelector('canvas')
if (canvas) return canvas.getBoundingClientRect().width
return window.innerWidth
}
function resizeAllBuffers(w, h) {
// The render canvas is off-DOM (the DOM holds the square display canvas),
// so reach it via the shim global rather than querySelector.
const mainCanvas = _mainCanvas
if (mainCanvas) {
mainCanvas.width = w
mainCanvas.height = h
width = w
height = h
_designScale = w / 750
if (_mainPG) {
_mainPG.width = w
_mainPG.height = h
_mainPG.canvas = mainCanvas
_mainPG.drawingContext = mainCanvas.getContext('2d')
_mainPG.drawingContext.imageSmoothingEnabled = false
window.drawingContext = _mainPG.drawingContext
}
}
if (_displayCanvas) {
_displayCanvas.width = w
_displayCanvas.height = w
// Resizing resets 2d context state — restore nearest-neighbor blits.
_displayCtx.imageSmoothingEnabled = false
}
staticLayerCache = createGraphics(w, h)
staticLayerCache.pixelDensity(1)
staticLayerCache.colorMode(HSB, 360, 100, 100, 100)
formPart1Cache = createGraphics(w, h)
formPart1Cache.pixelDensity(1)
formPart1Cache.colorMode(HSB, 360, 100, 100, 100)
formPart2Cache = createGraphics(w, h)
formPart2Cache.pixelDensity(1)
formPart2Cache.colorMode(HSB, 360, 100, 100, 100)
maskSilhouetteCache = createGraphics(w, h)
maskSilhouetteCache.pixelDensity(1)
fillBuffer = createGraphics(w, h)
fillBuffer.pixelDensity(1)
fillBuffer.colorMode(HSB, 360, 100, 100, 100)
fillPersistentLayer = createGraphics(w, h)
fillPersistentLayer.pixelDensity(1)
fillPersistentLayer.colorMode(HSB, 360, 100, 100, 100)
eyesCache = createGraphics(w, h)
eyesCache.pixelDensity(1)
eyesCache.colorMode(HSB, 360, 100, 100, 100)
eyesBlockingCache = createGraphics(w, h)
eyesBlockingCache.pixelDensity(1)
eyesBlockingCache.colorMode(HSB, 360, 100, 100, 100)
omniaEyesBase = createGraphics(w, h)
omniaEyesBase.pixelDensity(1)
omniaEyesBase.colorMode(HSB, 360, 100, 100, 100)
orbitBuffer = createGraphics(w, h)
orbitBuffer.pixelDensity(1)
orbitBuffer.colorMode(HSB, 360, 100, 100, 100)
meltBuffer = createGraphics(w, h)
meltBuffer.pixelDensity(1)
meltBuffer.colorMode(HSB, 360, 100, 100, 100)
corruptBuffer = createGraphics(w, h)
corruptBuffer.pixelDensity(1)
corruptBuffer.colorMode(HSB, 360, 100, 100, 100)
clipBuffer = createGraphics(w, h)
clipBuffer.pixelDensity(1)
clipBuffer.colorMode(HSB, 360, 100, 100, 100)
finalComposite = createGraphics(w, h)
finalComposite.pixelDensity(1)
finalComposite.colorMode(HSB, 360, 100, 100, 100)
finalShadedBuffer = createGraphics(w, h, WEBGL)
finalShadedBuffer.pixelDensity(1)
finalShadedBuffer.noStroke()
finalShadedBuffer.rectMode(CENTER)
asciiShader = finalShadedBuffer.createShader(vertSrc, fragDigitalStitchOptimized)
}
function applyTier(tier) {
const prevTier = _currentTier
_currentTier = tier
_tierMaxAnims = tier.maxAnims
_tierGridDensity = tier.gridCols
_tierNoiseOverride = tier.noise ? null : false
frameRate(tier.fps)
if (!prevTier || prevTier.canvasW !== tier.canvasW) {
const newW = tier.canvasW
const newH = floor(newW * ASPECT_RATIO)
if (width !== newW && width > 0) {
resizeAllBuffers(newW, newH)
resetAndRegenerate()
}
}
}
function initTierObserver() {
// Capture is a one-shot static render. Skip the tier/visibility observers:
// the visibility observer in particular flips _running back on and restarts
// the draw loop when the canvas is on-screen, which would undo the capture
// freeze and resume animation (trail/persist-buffer shimmer, etc.).
if (typeof window.$art !== 'undefined' && window.$art.captureMode) return
if (typeof ResizeObserver === 'undefined') return
const canvas = document.querySelector('canvas')
if (!canvas) return
let _resizeTimeout = null
const observer = new ResizeObserver(() => {
clearTimeout(_resizeTimeout)
_resizeTimeout = setTimeout(() => {
const displayW = canvas.getBoundingClientRect().width
const tier = getTierForWidth(displayW)
if (!_currentTier || tier.canvasW !== _currentTier.canvasW) {
_pendingTier = tier // Defer — applied at start of next draw()
}
}, 150)
})
observer.observe(canvas)
if (typeof IntersectionObserver !== 'undefined') {
const visObserver = new IntersectionObserver((entries) => {
_running = entries[0].isIntersecting
if (_running) requestAnimationFrame(_loop)
}, { threshold: 0.05 })
visObserver.observe(canvas)
}
}
function setup() {
readSeed()
applySeed()
const initialTier = getTierForWidth(getDisplayWidth())
_currentTier = initialTier
_tierMaxAnims = initialTier.maxAnims
_tierGridDensity = initialTier.gridCols
_tierNoiseOverride = initialTier.noise ? null : false
const canvasW = initialTier.canvasW
let canvas = createCanvas(canvasW, floor(canvasW * ASPECT_RATIO))
pixelDensity(1)
frameRate(initialTier.fps)
colorMode(HSB, 360, 100, 100, 100)
drawingContext.imageSmoothingEnabled = false
_designScale = width / 750
animationPool = new AnimationPool()
TRAITS = buildTraits()
bwMode = TRAITS.paletteName === "Etched"
PARAMS = resolveParams(TRAITS)
staticLayerCache = createGraphics(width, height)
staticLayerCache.pixelDensity(1)
staticLayerCache.colorMode(HSB, 360, 100, 100, 100)
formPart1Cache = createGraphics(width, height)
formPart1Cache.pixelDensity(1)
formPart1Cache.colorMode(HSB, 360, 100, 100, 100)
formPart2Cache = createGraphics(width, height)
formPart2Cache.pixelDensity(1)
formPart2Cache.colorMode(HSB, 360, 100, 100, 100)
maskSilhouetteCache = createGraphics(width, height)
maskSilhouetteCache.pixelDensity(1)
fillBuffer = createGraphics(width, height)
fillBuffer.pixelDensity(1)
fillBuffer.colorMode(HSB, 360, 100, 100, 100)
fillPersistentLayer = createGraphics(width, height)
fillPersistentLayer.pixelDensity(1)
fillPersistentLayer.colorMode(HSB, 360, 100, 100, 100)
eyesCache = createGraphics(width, height)
eyesCache.pixelDensity(1)
eyesCache.colorMode(HSB, 360, 100, 100, 100)
eyesBlockingCache = createGraphics(width, height)
eyesBlockingCache.pixelDensity(1)
eyesBlockingCache.colorMode(HSB, 360, 100, 100, 100)
omniaEyesBase = createGraphics(width, height)
omniaEyesBase.pixelDensity(1)
omniaEyesBase.colorMode(HSB, 360, 100, 100, 100)
orbitBuffer = createGraphics(width, height)
orbitBuffer.pixelDensity(1)
orbitBuffer.colorMode(HSB, 360, 100, 100, 100)
meltBuffer = createGraphics(width, height)
meltBuffer.pixelDensity(1)
meltBuffer.colorMode(HSB, 360, 100, 100, 100)
corruptBuffer = createGraphics(width, height)
corruptBuffer.pixelDensity(1)
corruptBuffer.colorMode(HSB, 360, 100, 100, 100)
clipBuffer = createGraphics(width, height)
clipBuffer.pixelDensity(1)
clipBuffer.colorMode(HSB, 360, 100, 100, 100)
finalComposite = createGraphics(width, height)
finalComposite.pixelDensity(1)
finalComposite.colorMode(HSB, 360, 100, 100, 100)
finalShadedBuffer = createGraphics(width, height, WEBGL)
finalShadedBuffer.pixelDensity(1)
finalShadedBuffer.noStroke()
finalShadedBuffer.rectMode(CENTER)
asciiShader = finalShadedBuffer.createShader(vertSrc, fragDigitalStitchOptimized)
layoutMasks()
calculateMaskBounds()
drawStaticLayers()
initTopology()
initOrbitingElements()
logTraits()
initTierObserver()
}
function getBackgroundColor() {
const bgType = PARAMS.background
if (bgType === "Halo" || bgType === "Void") {
return color(0, 0, 75)
} else if (bgType === "Aero") {
return color(200, 15, 85)
} else if (bgType === "Rose") {
return color(330, 25, 90)
} else if (bgType === "Mint") {
return color(150, 20, 88)
} else if (bgType === "Mist") {
return color(270, 18, 88)
} else if (bgType === "Rise") {
return color(20, 30, 90)
} else if (bgType === "Fallen") {
return color(0, 100, 90)
} else {
return color(PARAMS.palette[0])
}
}
function drawStaticLayers() {
if (!isDirty) return
staticLayerCache.clear()
drawBackground(staticLayerCache, PARAMS.background)
if (isTwoPartArchetype(TRAITS.archetype)) {
formPart2Cache.clear()
formPart2Cache.background(0, 0, 0, 0)
drawArchetypePart2(formPart2Cache, TRAITS.count, TRAITS.archetype)
formPart1Cache.clear()
formPart1Cache.background(0, 0, 0, 0)
drawArchetypePart1(formPart1Cache, TRAITS.count, TRAITS.archetype)
} else {
formPart2Cache.clear()
formPart2Cache.background(0, 0, 0, 0)
drawArchetype(formPart2Cache, TRAITS.count, TRAITS.archetype)
formPart1Cache.clear()
formPart1Cache.background(0, 0, 0, 0)
}
eyesCache.clear()
eyesCache.background(0, 0, 0, 0)
for (const m of masks) {
drawMaskEyes(eyesCache, m)
}
if (TRAITS.iris === "Omnia") {
omniaEyesBase.clear()
omniaEyesBase.background(0, 0, 0, 0)
for (const m of masks) {
drawMaskEyesOmniaBase(omniaEyesBase, m)
}
}
eyesBlockingCache.clear()
eyesBlockingCache.background(0, 0, 0, 0)
for (const m of masks) {
drawMaskEyesBlocking(eyesBlockingCache, m)
}
maskSilhouetteCache.clear()
maskSilhouetteCache.noStroke()
maskSilhouetteCache.fill(255)
for (const m of masks) drawMaskSilhouette(maskSilhouetteCache, m)
isDirty = false
}
function stableRemove(arr, index) {
arr.splice(index, 1)
}
function draw() {
if (_pendingTier) {
applyTier(_pendingTier)
_pendingTier = null
}
fillPersistentLayer.drawingContext.globalCompositeOperation = 'source-over'
fillPersistentLayer.drawingContext.globalAlpha = 1.0
clipBuffer.drawingContext.globalCompositeOperation = 'source-over'
clipBuffer.drawingContext.globalAlpha = 1.0
meltBuffer.drawingContext.globalCompositeOperation = 'source-over'
meltBuffer.drawingContext.globalAlpha = 1.0
corruptBuffer.drawingContext.globalCompositeOperation = 'source-over'
corruptBuffer.drawingContext.globalAlpha = 1.0
fillBuffer.drawingContext.globalCompositeOperation = 'source-over'
fillBuffer.drawingContext.globalAlpha = 1.0
finalComposite.drawingContext.globalCompositeOperation = 'source-over'
finalComposite.drawingContext.globalAlpha = 1.0
orbitBuffer.drawingContext.globalCompositeOperation = 'source-over'
orbitBuffer.drawingContext.globalAlpha = 1.0
if (!freezeMode) {
for (let i = fillAnimations.length - 1; i >= 0; i--) {
fillAnimations[i].update()
if (fillAnimations[i].isDone()) {
animationPool.release(fillAnimations[i])
stableRemove(fillAnimations, i)
}
}
for (let orb of orbitingElements) {
orb.update()
}
if (PARAMS.aura === "Radiant") {
starburstRotation += 0.0025
}
updateSkyfall()
updateMelt()
updateCorrupt()
updateVent()
}
if (!freezeMode) {
_cachedPart2Offset = formBreathOffsetPart2()
_cachedPart1Offset = formBreathOffsetPart1()
_cachedMaskOffset = maskBreathOffset()
}
const part2Offset = _cachedPart2Offset
const part1Offset = _cachedPart1Offset
const maskOffset = _cachedMaskOffset
fillPersistentLayer.clear()
fillPersistentLayer.background(0, 0, 0, 0)
for (let s of fillAnimations) {
fillBuffer.clear()
s.display(fillBuffer)
fillPersistentLayer.image(fillBuffer, 0, 0)
}
if (!freezeMode) spawner()
if (PARAMS.aura !== "CorruptMask") {
const clipCtx = clipBuffer.drawingContext
clipBuffer.clear()
clipCtx.save()
clipCtx.translate(0, maskOffset)
clipCtx.drawImage(maskSilhouetteCache.canvas, 0, 0)
clipCtx.restore()
fillPersistentLayer.push()
fillPersistentLayer.drawingContext.save()
fillPersistentLayer.drawingContext.globalCompositeOperation = 'destination-in'
fillPersistentLayer.drawingContext.drawImage(clipBuffer.canvas, 0, 0)
fillPersistentLayer.drawingContext.restore()
fillPersistentLayer.pop()
}
orbitBuffer.clear()
orbitBuffer.background(0, 0, 0, 0)
if (PARAMS.aura === "Radiant") {
drawStarburst(orbitBuffer)
}
if (PARAMS.aura === "Skyfall") {
drawSkyfall(orbitBuffer)
}
if (PARAMS.aura === "Vent") {
drawVent(orbitBuffer)
}
if (PARAMS.aura === "Kintsugi") {
drawCrack(orbitBuffer)
}
for (let orb of orbitingElements) {
orb.display(orbitBuffer, true)
}
if (!freezeMode) {
_cachedEyeOpacity = getEyeOpacity()
if (TRAITS.iris === "Omnia") _cachedOmniaCol = getOmniaColor()
}
finalComposite.clear()
finalComposite.background(getBackgroundColor())
if (PARAMS.aura === "CorruptRealm") {
corruptBuffer.clear()
corruptBuffer.background(0, 0, 0, 0)
corruptBuffer.image(staticLayerCache, 0, 0)
for (let slice of corruptSlices) {
if (slice.active) {
corruptBuffer.noStroke()
corruptBuffer.fill(0, 0, 0, 0)
corruptBuffer.blendMode(REPLACE)
corruptBuffer.rect(0, slice.y, width, slice.sliceHeight)
corruptBuffer.blendMode(BLEND)
let sliceImg = staticLayerCache.get(0, slice.y, width, slice.sliceHeight)
corruptBuffer.image(sliceImg, slice.offset, slice.y)
}
}
finalComposite.image(corruptBuffer, 0, 0)
} else {
finalComposite.image(staticLayerCache, 0, 0)
}
finalComposite.image(orbitBuffer, 0, 0)
finalComposite.push()
finalComposite.translate(0, part2Offset)
finalComposite.image(formPart2Cache, 0, 0)
finalComposite.pop()
finalComposite.push()
finalComposite.translate(0, part1Offset)
finalComposite.image(formPart1Cache, 0, 0)
finalComposite.pop()
if (PARAMS.aura === "Melt") {
meltBuffer.clear()
meltBuffer.background(0, 0, 0, 0)
drawMelt(meltBuffer)
meltBuffer.push()
meltBuffer.drawingContext.save()
meltBuffer.drawingContext.globalCompositeOperation = 'destination-out'
meltBuffer.push()
meltBuffer.translate(0, maskOffset)
meltBuffer.image(eyesBlockingCache, 0, 0)
meltBuffer.pop()
meltBuffer.drawingContext.restore()
meltBuffer.pop()
finalComposite.image(meltBuffer, 0, 0)
}
if (PARAMS.aura === "CorruptMask") {
meltBuffer.clear()
meltBuffer.background(0, 0, 0, 0)
meltBuffer.image(fillPersistentLayer, 0, 0)
meltBuffer.push()
meltBuffer.drawingContext.save()
meltBuffer.drawingContext.globalCompositeOperation = 'destination-in'
meltBuffer.push()
meltBuffer.translate(0, maskOffset)
meltBuffer.image(maskSilhouetteCache, 0, 0)
meltBuffer.pop()
meltBuffer.drawingContext.restore()
meltBuffer.pop()
corruptBuffer.clear()
corruptBuffer.background(0, 0, 0, 0)
corruptBuffer.image(meltBuffer, 0, 0)
corruptBuffer.push()
corruptBuffer.translate(0, maskOffset)
corruptBuffer.image(eyesBlockingCache, 0, 0)
corruptBuffer.pop()
const eyeOpacity = _cachedEyeOpacity
if (eyeOpacity > 0) {
corruptBuffer.push()
corruptBuffer.translate(0, maskOffset)
if (TRAITS.iris === "Omnia") {
const omniaCol = _cachedOmniaCol || getOmniaColor()
corruptBuffer.tint(red(omniaCol), green(omniaCol), blue(omniaCol), eyeOpacity * 255)
corruptBuffer.image(omniaEyesBase, 0, 0)
} else {
corruptBuffer.tint(255, eyeOpacity * 255)
corruptBuffer.image(eyesCache, 0, 0)
}
corruptBuffer.pop()
corruptBuffer.noTint()
}
for (let slice of corruptSlices) {
if (slice.active) {
corruptBuffer.noStroke()
corruptBuffer.fill(0, 0, 0, 0)
corruptBuffer.blendMode(REPLACE)
corruptBuffer.rect(slice.sliceX, slice.y, slice.sliceWidth, slice.sliceHeight)
corruptBuffer.blendMode(BLEND)
let sliceImg = fillPersistentLayer.get(slice.sliceX, slice.y, slice.sliceWidth, slice.sliceHeight)
corruptBuffer.image(sliceImg, slice.sliceX + slice.offset, slice.y)
}
}
finalComposite.image(corruptBuffer, 0, 0)
} else {
finalCompo