Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384// the avatar worker imports this file too and draws the same gradient
// 32-bit FNV-1aexport const hashString = (str: string): number => { let hash = 2166136261; for (let i = 0; i < str.length; i++) { hash ^= str.charCodeAt(i); hash = Math.imul(hash, 16777619); } return hash >>> 0;};
// mulberry32const mulberry32 = (seed: number) => { let a = seed >>> 0; return () => { a = (a + 0x6d2b79f5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };};
const round = (n: number, places = 1) => Number(n.toFixed(places));
// the chromatic entries of base16 material darker, in wheel order. a run of them stays harmoniousconst PALETTE = ["#f07178", "#f78c6c", "#ffcb6b", "#c3e88d", "#89ddff", "#82aaff", "#c792ea"];
export interface Gradient { angle: number; // offsets are percentages stops: { offset: number; color: string }[]; glow: { x: number; y: number; r: number; opacity: number; color: string };}
export const gradientFor = (seed: string): Gradient => { const rand = mulberry32(hashString(seed));
const angle = Math.round(rand() * 360); const stopCount = 3 + Math.floor(rand() * 3); const direction = rand() < 0.5 ? -1 : 1; const start = Math.floor(rand() * PALETTE.length);
const stops = Array.from({ length: stopCount }, (_, i) => ({ offset: round((i / (stopCount - 1)) * 100), color: PALETTE[ (((start + direction * i) % PALETTE.length) + PALETTE.length) % PALETTE.length ] }));
const glow = { x: round(0.2 + rand() * 0.6, 2), y: round(0.15 + rand() * 0.5, 2), r: round(0.55 + rand() * 0.35, 2), opacity: round(0.25 + rand() * 0.2, 2), color: PALETTE[Math.floor(rand() * PALETTE.length)] };
return { angle, stops, glow };};
// ogre's satori reads rgb(r g b / 0) as opaque black but handles hex alphaconst alpha = (opacity: number) => Math.round(opacity * 255) .toString(16) .padStart(2, "0");
// the same picture as the worker's svg, as a css background-imageexport const gradientCss = (seed: string): string => { const { angle, stops, glow } = gradientFor(seed); // the svg gradient line runs corner to corner whatever the angle. css spans the box along its angle const radians = ((angle + 135) * Math.PI) / 180; const scale = Math.SQRT2 / (Math.abs(Math.sin(radians)) + Math.abs(Math.cos(radians))); const linear = stops .map((s) => `${s.color} ${round(50 + (s.offset - 50) * scale)}%`) .join(", "); const radius = round(glow.r * 100); return ( `radial-gradient(${radius}% ${radius}% at ${round(glow.x * 100)}% ${round(glow.y * 100)}%, ` + `${glow.color}${alpha(glow.opacity)}, ${glow.color}00), ` + `linear-gradient(${(angle + 135) % 360}deg, ${linear})` );};