* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
body {
background: radial-gradient(circle at center, #878889, #2f353f);
display: grid;
grid-template-rows: 1fr min-content;
place-items: center;
}
.ui {
padding: 1rem;
}
button {
background: hsl(38, 1%, 95%);
border: none;
border-radius: 0.2rem;
color: #2f353f;
padding: 0.5rem;
}
View Compiled
const zinc = "#EEEBEB";
const paynes = "#29313E";
const cobaltTeal = "#38B0C1";
const peachFuzz = "#FFBE98";
const weeklyColors = [
cobaltTeal,
peachFuzz
];
const dailyColors = [
"#D95179",
"#79244E",
"#AB9E3A",
"#803B0C",
"#E98065"
];
let colors = weeklyColors.concat(dailyColors);
colors = colors.map((color) => tinycolor(color).toHsl());
colors = colors.filter(color => {
return color.h > 0 && color.s > 0.07
});
colors = colors.sort((a, b) => {
return b.l - a.l;
});
colors = colors.map((color) => tinycolor(color).toHslString());
const modulateColor = (baseColor, hRange = 8, sRange = 8, lRange = 8) => {
const random = (min, max) => Math.random() * (max - min) + min;
baseColor = tinycolor(baseColor).toHsl();
const h = Math.floor(baseColor.h + random(-hRange, hRange));
const s = Math.floor(baseColor.s * 100 + random(-sRange, sRange));
const l = Math.floor(baseColor.l * 100 + random(-lRange, lRange));
return `hsl(${h}, ${s}%, ${l}%)`;
};
P5Capture.setDefaultOptions({
disableUi: true,
duration: 300,
format: "gif",
framerate: 60,
});
let scaleOutput = 1;
let dimensions = {
x: 800,
y: 800
}
let frame;
let output, canvas;
let seed;
const phi = 1.618;
const fibb = [3, 5, 8, 13, 21];
let crystalSize, gridSize, margin, numColumns, numRows, gridBox;
let rows = [];
function setup () {
// print or save size
output = createGraphics(dimensions.x, dimensions.y);
canvas = createCanvas(dimensions.x, dimensions.y);
colorMode(HSB, 360, 100, 100, 1);
rectMode(CENTER);
imageMode(CENTER);
pixelDensity(1);
noLoop();
frame = 72 / 2;
numColumns = 3;
numRows = numColumns * 2;
gridBox = width / numColumns;
crystalSize = gridBox / phi;
resetSketch();
let div = createDiv('');
div.class('ui');
let resetButton = createButton("Reset");
resetButton.parent(div);
resetButton.mousePressed(resetSketch);
// scale canvas to fit on screen
const canvasElement = document.querySelector(".p5Canvas");
const canvasRatio = canvasElement.height / canvasElement.width;
const windowRatio = window.innerHeight / window.innerWidth;
let cssHeight;
let cssWidth;
if (windowRatio < canvasRatio) {
cssHeight = window.innerHeight;
cssWidth = cssHeight / canvasRatio;
} else {
cssWidth = window.innerWidth;
cssHeight = cssWidth * canvasRatio;
}
canvasElement.style.width = `${cssWidth * 0.85}px`;
canvasElement.style.height = `${cssHeight * 0.85}px`;
}
const createCrystal = (oCrystal, index, gridRow) => {
const crystal = oCrystal;
crystal.layerConstructors = [
{
name: "outlineShape",
function: (oCrystal) => outlineShape(oCrystal),
weight: 0.3
},
{
name: "steppedHexagons",
function: (oCrystal) => steppedHexagons(oCrystal),
weight: 0.75
},
{
name: "circles",
function: (oCrystal) => circles(oCrystal),
weight: 0.5
},
{
name: "simpleLines",
function: (oCrystal) => simpleLines(oCrystal),
weight: 0.3
},
{
name: "centeredShape",
function: (oCrystal) => centeredShape(oCrystal),
weight: 0.3
},
{
name: "dottedLines",
function: (oCrystal) => dottedLines(oCrystal),
weight: 0.3
},
]
if (crystal.x < width && crystal.y < height) {
crystal.sides = random([6, 8, 10, 12]);
crystal.angle = TWO_PI / crystal.sides;
crystal.numSteps = random(fibb);
crystal.singleStep = (crystalSize / 2) / crystal.numSteps;
crystal.palette = [modulateColor(random(colors)), modulateColor(random(colors))];
crystal.shapePicker = floor(random(0, 4));
crystal.layers = [];
crystal.layerConstructors.forEach(layer => {
const picker = random();
if (picker > layer.weight) {
crystal.layers.push(layer);
}
});
} else if (crystal.x === width && crystal.y < height) {
const reference = gridRow[index - numColumns];
crystal.sides = reference.sides;
crystal.angle = reference.angle;
crystal.numSteps = reference.numSteps;
crystal.singleStep = reference.singleStep;
crystal.palette = reference.palette;
crystal.shapePicker = reference.shapePicker;
crystal.layers = reference.layers;
} else {
const reference = rows[0][index];
crystal.sides = reference.sides;
crystal.angle = reference.angle;
crystal.numSteps = reference.numSteps;
crystal.singleStep = reference.singleStep;
crystal.palette = reference.palette;
crystal.shapePicker = reference.shapePicker;
crystal.layers = reference.layers;
}
crystal.layers.forEach(layer => {
layer.function(crystal);
});
// testLines(crystal);
}
const outlineShape = (oCrystal) => {
const hexagonTrue = oCrystal.shapePicker < 3? true : false;
noFill();
if(oCrystal.shapePicker === 0 || oCrystal.shapePicker === 2) {
strokeWeight(2);
}
stroke(oCrystal.palette[0]);
push();
translate(oCrystal.x, oCrystal.y);
if(hexagonTrue) {
hexagon(0, 0, crystalSize / 2);
} else {
circle(0, 0, crystalSize);
}
pop();
}
const steppedHexagons = (oCrystal) => {
const centerOffset = (crystalSize / 2) * ((phi - 1) * (phi - 1));
const singleStep = ((crystalSize / 2) - centerOffset) / oCrystal.numSteps;
noFill();
stroke(oCrystal.palette[1]);
strokeWeight(1);
push();
translate(oCrystal.x, oCrystal.y);
for (let i = 0; i < oCrystal.numSteps; i++) {
hexagon(0, 0, centerOffset + (i * singleStep));
}
pop();
}
const circles = (oCrystal) => {
const shapeSize = crystalSize / 2;
const position = crystalSize / 2 - shapeSize / 2;
noFill();
strokeWeight(1);
stroke(oCrystal.palette[0]);
push();
translate(oCrystal.x, oCrystal.y);
for(let i = 0; i <= 6; i++) {
circle(position, 0, shapeSize);
rotate(PI / 3);
}
pop();
}
const simpleLines = (oCrystal) => {
const start = floor(oCrystal.numSteps / 2);
const stop = start + oCrystal.shapePicker;
noFill();
if (oCrystal.shapePicker < 3) {
strokeWeight(2);
} else {
strokeWeight(1);
}
stroke(oCrystal.palette[1]);
if(stop > start) {
push();
translate(oCrystal.x, oCrystal.y);
for (let i = 0; i < oCrystal.sides; i++) {
line(start * oCrystal.singleStep, 0, min(stop * oCrystal.singleStep, crystalSize / 2), 0);
rotate(oCrystal.angle);
}
pop();
}
}
const centeredShape = (oCrystal) => {
const shapeSize = (oCrystal.singleStep * oCrystal.numSteps + oCrystal.singleStep * oCrystal.shapePicker) / phi;
noStroke();
fill(oCrystal.palette[0]);
push();
translate(oCrystal.x, oCrystal.y);
switch (oCrystal.shapePicker) {
case 0:
square(0, 0, shapeSize);
break;
case 1:
circle(0, 0, shapeSize);
break;
case 2:
hexagon(0, 0, shapeSize / phi);
break;
case 3:
push();
rotate(PI / 6);
hexagon(0, 0, shapeSize / phi);
pop();
break;
default:
hexagon(0, 0, shapeSize / phi);
}
pop();
}
const dottedLines = (oCrystal) => {
noFill();
strokeWeight(floor(2 * phi));
stroke(oCrystal.palette[1]);
push();
translate(oCrystal.x, oCrystal.y);
for(let i = 0; i <= oCrystal.sides; i++) {
for(let x = oCrystal.singleStep; x < crystalSize / 2; x += oCrystal.singleStep) {
point(x, 0);
}
rotate(oCrystal.angle);
}
pop();
}
const testLines = (oCrystal) => {
noFill();
stroke(0, 0, 70);
strokeWeight(1);
push();
translate(oCrystal.x, oCrystal.y);
circle(0, 0, crystalSize);
for(let i = 0; i < oCrystal.sides; i++) {
line(0, 0, 0, crystalSize / 2);
rotate(oCrystal.angle);
}
pop();
}
const createRow = (y, posY) => {
const row = [];
let posX;
if(y % 2 === 0) {
for (let x = 0; x <= numColumns; x++) {
posX = x * gridBox;
let crystal = {};
crystal.x = posX;
crystal.y = posY;
row.push(crystal);
}
} else {
for (let x = 0; x <= numColumns - 1; x++) {
posX = x * gridBox + (gridBox / 2);
crystal = {};
crystal.x = posX;
crystal.y = posY;
row.push(crystal);
}
}
rows.push(row);
}
const createGrid = () => {
for (let y = 0; y <= numRows; y++) {
let yPos = y * (gridBox / 2);
createRow(y, yPos);
}
rows.forEach(row => {
row.forEach((location, index, row) => {
createCrystal(location, index, row);
// console.log(location);
});
});
}
const resetSketch = () => {
background(zinc);
seed = floor(random(99999));
randomSeed(seed);
console.log(seed);
rows = [];
createGrid();
}
function draw () {
output.clear()
output.push();
output.scale(scaleOutput);
output.pop();
image(output, 0, 0);
}
function keyPressed() {
if(key === 's') {
const capture = P5Capture.getInstance();
capture.start();
}
}
// Utility functions
const randomSelectTwo = () => {
const rand = random();
if( rand > 0.5 ) {
return true;
} else {
return false;
}
}
const pointOnCircle = (posX, posY, radius, angle) => {
const x = posX + radius * cos(angle);
const y = posY + radius * sin(angle);
return createVector(x, y);
}
const hexagon = (posX, posY, radius) => {
const angle = TWO_PI / 6;
beginShape();
for(let i = 0; i < 6; i++) {
const thisVertex = pointOnCircle(posX, posY, radius, i * angle);
vertex(thisVertex.x, thisVertex.y);
}
endShape(CLOSE);
}
View Compiled
This Pen doesn't use any external CSS resources.