gardenconst baseGridDensity = 60;
const baseWeaveSteps = 10;
const baseWeaveSpeed = 1.0;
const baseStrokeWidth = 1.0;
let angleNoiseScale = 0.015;
let sizeNoiseScale = 0.06;
let recursionDepth = 3;
let recursionFactor = 0.4;
let grainAmount = 25;
const gardenPalettes = {
original: [
{ h: [330, 360], s: [70, 90], b: [60, 80] },
{ h: [190, 230], s: [80, 100], b: [70, 90] },
{ h: [40, 60], s: [60, 80], b: [30, 50] },
{ h: [260, 290], s: [70, 90], b: [40, 60] },
],
'original-rose': [
{ h: [330, 360], s: [70, 90], b: [60, 80] },
{ h: [4, 22], s: [76, 94], b: [68, 94] },
{ h: [34, 52], s: [66, 88], b: [56, 86] },
{ h: [92, 124], s: [56, 78], b: [30, 62] },
{ h: [260, 290], s: [50, 72], b: [48, 78] },
{ h: [330, 360], s: [4, 14], b: [5, 20] },
],
'original-water': [
{ h: [190, 230], s: [80, 100], b: [70, 90] },
{ h: [166, 194], s: [68, 90], b: [48, 80] },
{ h: [92, 124], s: [54, 76], b: [30, 62] },
{ h: [328, 350], s: [54, 76], b: [58, 88] },
{ h: [38, 56], s: [64, 86], b: [54, 84] },
{ h: [194, 230], s: [4, 14], b: [5, 20] },
],
'original-gold': [
{ h: [40, 60], s: [60, 80], b: [30, 50] },
],
'original-violet': [
{ h: [260, 290], s: [70, 90], b: [40, 60] },
],
'night-bloom': [
{ h: [285, 320], s: [70, 90], b: [60, 80] },
{ h: [215, 245], s: [80, 100], b: [70, 90] },
{ h: [165, 190], s: [60, 80], b: [30, 50] },
{ h: [325, 350], s: [70, 90], b: [40, 60] },
],
'sunlit-earth': [
{ h: [5, 25], s: [70, 90], b: [60, 80] },
{ h: [28, 48], s: [80, 100], b: [70, 90] },
{ h: [50, 72], s: [60, 80], b: [30, 50] },
{ h: [338, 358], s: [70, 90], b: [40, 60] },
],
'pond-glass': [
{ h: [145, 175], s: [70, 90], b: [60, 80] },
{ h: [178, 205], s: [80, 100], b: [70, 90] },
{ h: [205, 225], s: [60, 80], b: [30, 50] },
{ h: [95, 125], s: [70, 90], b: [40, 60] },
],
'orchard-dusk': [
{ h: [345, 360], s: [70, 90], b: [60, 80] },
{ h: [0, 18], s: [80, 100], b: [70, 90] },
{ h: [72, 100], s: [60, 80], b: [30, 50] },
{ h: [275, 305], s: [70, 90], b: [40, 60] },
],
'spring-border': [
{ h: [328, 350], s: [70, 90], b: [60, 80] },
{ h: [42, 62], s: [80, 100], b: [70, 90] },
{ h: [92, 126], s: [60, 80], b: [30, 50] },
{ h: [182, 208], s: [70, 90], b: [40, 60] },
],
'wildflower-meadow': [
{ h: [0, 18], s: [70, 90], b: [60, 80] },
{ h: [202, 232], s: [80, 100], b: [70, 90] },
{ h: [42, 58], s: [60, 80], b: [30, 50] },
{ h: [262, 294], s: [70, 90], b: [40, 60] },
],
'herb-garden': [
{ h: [92, 118], s: [70, 90], b: [60, 80] },
{ h: [70, 96], s: [80, 100], b: [70, 90] },
{ h: [154, 184], s: [60, 80], b: [30, 50] },
{ h: [242, 270], s: [70, 90], b: [40, 60] },
],
'kitchen-garden': [
{ h: [0, 14], s: [70, 90], b: [60, 80] },
{ h: [104, 136], s: [80, 100], b: [70, 90] },
{ h: [42, 60], s: [60, 80], b: [30, 50] },
{ h: [274, 302], s: [70, 90], b: [40, 60] },
],
'water-garden': [
{ h: [328, 350], s: [70, 90], b: [60, 80] },
{ h: [180, 210], s: [80, 100], b: [70, 90] },
{ h: [72, 104], s: [60, 80], b: [30, 50] },
{ h: [246, 276], s: [70, 90], b: [40, 60] },
],
'rose-arbor': [
{ h: [342, 360], s: [70, 90], b: [60, 80] },
{ h: [8, 28], s: [80, 100], b: [70, 90] },
{ h: [304, 332], s: [60, 80], b: [30, 50] },
{ h: [88, 120], s: [70, 90], b: [40, 60] },
],
'blush-garden': [
{ h: [342, 358], s: [22, 38], b: [80, 96] },
{ h: [12, 28], s: [24, 42], b: [82, 98] },
{ h: [88, 112], s: [18, 34], b: [72, 90] },
{ h: [252, 276], s: [20, 38], b: [76, 94] },
{ h: [110, 134], s: [58, 78], b: [28, 52] },
{ h: [330, 360], s: [5, 16], b: [6, 24] },
],
'morning-border': [
{ h: [44, 58], s: [20, 36], b: [84, 98] },
{ h: [194, 212], s: [18, 34], b: [80, 96] },
{ h: [132, 154], s: [16, 32], b: [76, 92] },
{ h: [326, 344], s: [20, 38], b: [82, 97] },
{ h: [126, 150], s: [60, 80], b: [26, 50] },
{ h: [198, 222], s: [6, 18], b: [6, 22] },
],
'hydrangea-mist': [
{ h: [218, 238], s: [24, 42], b: [78, 94] },
{ h: [268, 290], s: [20, 38], b: [80, 96] },
{ h: [194, 214], s: [18, 34], b: [82, 98] },
{ h: [316, 338], s: [20, 36], b: [78, 95] },
{ h: [98, 126], s: [56, 76], b: [26, 50] },
{ h: [242, 270], s: [8, 20], b: [6, 22] },
],
'cottage-pastel': [
{ h: [4, 18], s: [28, 46], b: [80, 96] },
{ h: [44, 58], s: [22, 40], b: [82, 98] },
{ h: [90, 114], s: [18, 34], b: [72, 90] },
{ h: [204, 226], s: [24, 42], b: [76, 94] },
{ h: [88, 118], s: [58, 78], b: [28, 52] },
{ h: [0, 20], s: [5, 16], b: [6, 22] },
],
'pressed-flowers': [
{ h: [336, 352], s: [24, 42], b: [66, 84] },
{ h: [36, 52], s: [28, 46], b: [70, 88] },
{ h: [80, 104], s: [18, 34], b: [62, 80] },
{ h: [262, 284], s: [22, 40], b: [66, 84] },
{ h: [68, 94], s: [46, 66], b: [24, 46] },
{ h: [248, 280], s: [4, 14], b: [5, 20] },
],
'glasshouse-light': [
{ h: [124, 150], s: [18, 34], b: [78, 94] },
{ h: [174, 196], s: [18, 32], b: [82, 98] },
{ h: [14, 30], s: [22, 40], b: [82, 98] },
{ h: [254, 278], s: [20, 36], b: [78, 95] },
{ h: [142, 166], s: [62, 82], b: [24, 48] },
{ h: [178, 210], s: [5, 16], b: [5, 21] },
],
};
const gardenPaletteNames = Object.keys(gardenPalettes);
const softGardenPaletteNames = new Set([
'blush-garden',
'morning-border',
'hydrangea-mist',
'cottage-pastel',
'pressed-flowers',
'glasshouse-light',
]);
const mixedGardenPaletteNames = new Set([
'original-rose',
'original-water',
]);
let colorPalette;
let paletteName;
let softPaletteActive = false;
let mixedPaletteActive = false;
let lightVector;
let seed;
const FALLBACK_SEED = 949032;
const MAX_SEED = 999999;
const DEFAULT_SIZE = 1000;
const RENDER_PIXEL_DENSITY = 2;
let WIDTH = window.innerWidth;
let HEIGHT = window.innerHeight;
let DIM = Math.min(WIDTH, HEIGHT);
let M = DIM / DEFAULT_SIZE;
function readArtworkSeed() {
const raw = window.ARTSCRIPT_SEED ?? window.__TOKEN_STUDIO_SEED ?? new URLSearchParams(window.location.search).get('seed');
if (raw === undefined || raw === null || !/^[0-9]+$/.test(String(raw).trim())) {
return FALLBACK_SEED;
}
return Number(BigInt(String(raw).trim()) % BigInt(MAX_SEED + 1));
}
function selectPaletteName(nextSeed) {
const requestedName = new URLSearchParams(window.location.search).get('palette');
if (requestedName && Object.hasOwn(gardenPalettes, requestedName)) {
return requestedName;
}
return gardenPaletteNames[nextSeed % gardenPaletteNames.length];
}
function setup() {
document.body.style.margin = '0';
document.body.style.padding = '0';
document.body.style.overflow = 'hidden';
document.body.style.backgroundColor = '#111';
document.body.style.display = 'flex';
document.body.style.justifyContent = 'center';
document.body.style.alignItems = 'center';
document.body.style.height = '100vh';
DIM = Math.min(WIDTH, HEIGHT);
const canvasSize = Math.max(1, Math.floor(DIM));
createCanvas(canvasSize, canvasSize);
pixelDensity(RENDER_PIXEL_DENSITY);
colorMode(HSB, 360, 100, 100, 100);
angleMode(RADIANS);
startGeneration(readArtworkSeed());
noLoop();
}
function startGeneration(nextSeed) {
seed = Number.isInteger(nextSeed) ? constrain(nextSeed, 0, MAX_SEED) : floor(random(1000000));
randomSeed(seed);
noiseSeed(seed);
if (width <= 0 || height <= 0) return;
const gridStep = (DIM / baseGridDensity);
const weaveSteps = baseWeaveSteps;
const weaveSpeed = baseWeaveSpeed * M;
const scaledBaseStrokeWidth = baseStrokeWidth * M;
lightVector = p5.Vector.fromAngle(random(TWO_PI));
lightVector.normalize();
paletteName = selectPaletteName(seed);
colorPalette = gardenPalettes[paletteName];
softPaletteActive = softGardenPaletteNames.has(paletteName);
mixedPaletteActive = mixedGardenPaletteNames.has(paletteName);
document.body.dataset.artSeed = String(seed);
document.body.dataset.palette = paletteName;
document.body.dataset.colorStyle = softPaletteActive
? 'soft'
: mixedPaletteActive
? 'mixed'
: 'legacy';
if (softPaletteActive) {
background(100, 12, 13);
} else if (mixedPaletteActive) {
background(100, 24, 9);
} else {
background(100, 40, 10);
}
for (let y = 0; y < height; y += gridStep) {
for (let x = 0; x < width; x += gridStep) {
let currentPos = createVector(x, y);
for (let i = 0; i < weaveSteps; i++) {
let virtualX = currentPos.x / M;
let virtualY = currentPos.y / M;
let angle = noise(virtualX * angleNoiseScale, virtualY * angleNoiseScale) * TWO_PI * 3;
let sizeFactor = noise(virtualX * sizeNoiseScale, virtualY * sizeNoiseScale);
let shapeSize = map(sizeFactor, 0, 1, 2 * M, gridStep * 2.5);
drawRecursiveShape(currentPos.x, currentPos.y, shapeSize, angle, recursionDepth, scaledBaseStrokeWidth);
currentPos.x += cos(angle) * weaveSpeed;
currentPos.y += sin(angle) * weaveSpeed;
}
}
}
applyGrain();
const infoDiv = document.querySelector('.info');
if (infoDiv) {
infoDiv.innerHTML = `Seed: ${seed} | Palette: ${paletteName} | Press 's' to save.`;
}
notifyRendered();
}
function notifyRendered() {
if (typeof window.__TOKEN_STUDIO_NOTIFY !== 'function') return;
window.requestAnimationFrame(() => {
window.__TOKEN_STUDIO_NOTIFY('token-art:rendered');
});
}
function drawRecursiveShape(x, y, size, angle, level, scaledStrokeWidth) {
if (level <= 0 || size < 0.5 * M) return;
push();
translate(x, y);
rotate(angle);
let normalVector = p5.Vector.fromAngle(angle);
let lightDot = normalVector.dot(lightVector);
let paletteIndex = floor(map(angle % TWO_PI, 0, TWO_PI, 0, colorPalette.length));
paletteIndex = constrain(paletteIndex, 0, colorPalette.length - 1);
let selectedPalette = colorPalette[paletteIndex];
let hue = map(x, 0, width, selectedPalette.h[0], selectedPalette.h[1]);
let hueShift = softPaletteActive
? map(lightDot, -1, 1, -8, 8)
: mixedPaletteActive
? map(lightDot, -1, 1, -12, 12)
: map(lightDot, -1, 1, -20, 20);
hue = (hue + hueShift + 360) % 360;
let saturation = 95;
let brightness = map(lightDot, -1, 1, 40, 100);
if (softPaletteActive || mixedPaletteActive) {
saturation = map(x, 0, width, selectedPalette.s[0], selectedPalette.s[1]);
brightness = map(lightDot, -1, 1, selectedPalette.b[0], selectedPalette.b[1]);
}
let sw = map(level, recursionDepth, 1, scaledStrokeWidth, scaledStrokeWidth * 0.1);
strokeWeight(sw);
let alpha = softPaletteActive
? map(lightDot, -1, 1, 22, 58)
: mixedPaletteActive
? map(lightDot, -1, 1, 26, 68)
: map(lightDot, -1, 1, 25, 70);
stroke(hue, saturation, brightness, alpha);
noFill();
rectMode(CENTER);
rect(0, 0, size * 1.8, size, size * 0.3);
let newSize = size * recursionFactor;
let angleOffset = map(sin(angle), -1, 1, -QUARTER_PI, QUARTER_PI);
let newAngle = angle + angleOffset;
drawRecursiveShape(0, 0, newSize, newAngle, level - 1, scaledStrokeWidth);
pop();
}
function applyGrain() {
loadPixels();
for (let i = 0; i < pixels.length; i += 4) {
let grain = random(-grainAmount, grainAmount);
pixels[i] = pixels[i] + grain;
pixels[i + 1] = pixels[i + 1] + grain;
pixels[i + 2] = pixels[i + 2] + grain;
}
updatePixels();
}
function keyPressed() {
if (key === 's' || key === 'S') {
saveCanvas('In the Garden - ' + seed, 'png');
}
}
function windowResized() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
DIM = Math.min(WIDTH, HEIGHT);
M = DIM / DEFAULT_SIZE;
const canvasSize = Math.max(1, Math.floor(DIM));
resizeCanvas(canvasSize, canvasSize);
startGeneration(seed);
}