Skip to content

Instantly share code, notes, and snippets.

@vikramrojo
Created May 6, 2025 19:21
Show Gist options
  • Save vikramrojo/bd9f3f98a3ff56286202fe012919f1ac to your computer and use it in GitHub Desktop.
Save vikramrojo/bd9f3f98a3ff56286202fe012919f1ac to your computer and use it in GitHub Desktop.
Guilloche p5js Script
let primaryColorPicker, secondaryColorPicker, diagonalColorPicker, polygonColorPicker, polygon2ColorPicker;
let vertThicknessSlider, horizThicknessSlider, diagThicknessSlider, polyThicknessSlider, poly2ThicknessSlider;
let vertAmplitudeSlider, horizAmplitudeSlider;
let vertDensitySlider, horizDensitySlider, diagDensitySlider;
let dashLengthSlider, dashGapSlider, diagonalAngleSlider;
let polyCountSlider, polySidesSlider, polyRadiusSlider;
let poly2SidesSlider, poly2RadiusSlider;
let polyDuplicatesSlider, polySpacingSlider;
let poly2DuplicatesSlider, poly2SpacingSlider;
let offsetSlider;
let downloadButton;
let verticalToggle, horizontalToggle, diagonalToggle, polygonToggle, polygon2Toggle, polyRotateToggle;
let tileWidth = 500;
let tileHeight = 500;
function setup() {
createCanvas(tileWidth * 2, tileHeight * 2);
noFill();
let yPos = 20;
let yStep = 35; // Reduced further to fit more controls
// Create toggles and color pickers without labels
verticalToggle = createCheckbox('', true);
verticalToggle.position(1020, yPos);
primaryColorPicker = createColorPicker('#FFA300');
primaryColorPicker.position(1060, yPos);
yPos += yStep;
horizontalToggle = createCheckbox('', true);
horizontalToggle.position(1020, yPos);
secondaryColorPicker = createColorPicker('#06B6D4');
secondaryColorPicker.position(1060, yPos);
yPos += yStep;
diagonalToggle = createCheckbox('', true);
diagonalToggle.position(1020, yPos);
diagonalColorPicker = createColorPicker('#B6E4C5');
diagonalColorPicker.position(1060, yPos);
yPos += yStep;
polygonToggle = createCheckbox('', true);
polygonToggle.position(1020, yPos);
polygonColorPicker = createColorPicker('#FFEA85'); // Yellow
polygonColorPicker.position(1060, yPos);
yPos += yStep;
polygon2Toggle = createCheckbox('', true);
polygon2Toggle.position(1020, yPos);
polygon2ColorPicker = createColorPicker('#A5F3FC'); // Light cyan
polygon2ColorPicker.position(1060, yPos);
yPos += yStep;
// Line thickness controls
createDiv('Vertical Line Thickness:').position(1020, yPos);
vertThicknessSlider = createSlider(1, 10, 2, 1);
vertThicknessSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Horizontal Line Thickness:').position(1020, yPos);
horizThicknessSlider = createSlider(1, 10, 2, 1);
horizThicknessSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Diagonal Line Thickness:').position(1020, yPos);
diagThicknessSlider = createSlider(1, 10, 1, 1);
diagThicknessSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 1 Line Thickness:').position(1020, yPos);
polyThicknessSlider = createSlider(1, 10, 1, 1);
polyThicknessSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 2 Line Thickness:').position(1020, yPos);
poly2ThicknessSlider = createSlider(1, 10, 1, 1);
poly2ThicknessSlider.position(1020, yPos + 20);
yPos += yStep;
// Wave and diagonal controls
createDiv('Vertical Amplitude:').position(1020, yPos);
vertAmplitudeSlider = createSlider(5, 50, 20, 1);
vertAmplitudeSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Horizontal Amplitude:').position(1020, yPos);
horizAmplitudeSlider = createSlider(5, 50, 20, 1);
horizAmplitudeSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Vertical Density:').position(1020, yPos);
vertDensitySlider = createSlider(1, 10, 3, 1);
vertDensitySlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Horizontal Density:').position(1020, yPos);
horizDensitySlider = createSlider(1, 10, 3, 1);
horizDensitySlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Diagonal Line Spacing:').position(1020, yPos);
diagDensitySlider = createSlider(5, 40, 20, 1);
diagDensitySlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Diagonal Angle (degrees):').position(1020, yPos);
diagonalAngleSlider = createSlider(0, 90, 45, 15);
diagonalAngleSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Dash Length:').position(1020, yPos);
dashLengthSlider = createSlider(5, 40, 15, 1);
dashLengthSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Dash Gap:').position(1020, yPos);
dashGapSlider = createSlider(5, 40, 10, 1);
dashGapSlider.position(1020, yPos + 20);
yPos += yStep;
// Polygon count and type toggle
createDiv('Polygon Count per Tile:').position(1020, yPos);
polyCountSlider = createSlider(1, 10, 3, 1);
polyCountSlider.position(1020, yPos + 20);
yPos += yStep;
// Moved Rotating Polygons toggle here
polyRotateToggle = createCheckbox('Rotating Polygons (vs Concentric)', false);
polyRotateToggle.position(1020, yPos);
yPos += yStep;
// Polygon controls - group 1
createDiv('---- Polygon 1 Settings ----').position(1020, yPos);
yPos += yStep;
createDiv('Polygon 1 Sides:').position(1020, yPos);
polySidesSlider = createSlider(3, 12, 6, 1);
polySidesSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 1 Radius:').position(1020, yPos);
polyRadiusSlider = createSlider(10, 80, 40, 2); // Steps of 2 pixels
polyRadiusSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 1 Duplicates:').position(1020, yPos);
polyDuplicatesSlider = createSlider(1, 9, 3, 2);
polyDuplicatesSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 1 Spacing/Rotation:').position(1020, yPos);
polySpacingSlider = createSlider(2, 30, 8, 2); // Steps of 2 pixels/degrees
polySpacingSlider.position(1020, yPos + 20);
yPos += yStep;
// Polygon controls - group 2
createDiv('---- Polygon 2 Settings ----').position(1020, yPos);
yPos += yStep;
createDiv('Polygon 2 Sides:').position(1020, yPos);
poly2SidesSlider = createSlider(3, 12, 3, 1);
poly2SidesSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 2 Radius:').position(1020, yPos);
poly2RadiusSlider = createSlider(10, 80, 30, 2); // Steps of 2 pixels
poly2RadiusSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 2 Duplicates:').position(1020, yPos);
poly2DuplicatesSlider = createSlider(1, 9, 5, 2);
poly2DuplicatesSlider.position(1020, yPos + 20);
yPos += yStep;
createDiv('Polygon 2 Spacing/Rotation:').position(1020, yPos);
poly2SpacingSlider = createSlider(2, 30, 6, 2); // Steps of 2 pixels/degrees
poly2SpacingSlider.position(1020, yPos + 20);
yPos += yStep;
// General settings
createDiv('Pattern Offset:').position(1020, yPos);
offsetSlider = createSlider(0, 20, 4, 2);
offsetSlider.position(1020, yPos + 20);
yPos += yStep;
// Create download button
downloadButton = createButton('Download Single Tileable PNG (500×500)');
downloadButton.position(1020, yPos + 20);
downloadButton.mousePressed(downloadSingleTile);
}
function draw() {
clear();
// Get values from sliders
let primaryColor = primaryColorPicker.color();
let secondaryColor = secondaryColorPicker.color();
let diagonalColor = diagonalColorPicker.color();
let polygonColor = polygonColorPicker.color();
let polygon2Color = polygon2ColorPicker.color();
let vertThickness = vertThicknessSlider.value();
let horizThickness = horizThicknessSlider.value();
let diagThickness = diagThicknessSlider.value();
let polyThickness = polyThicknessSlider.value();
let poly2Thickness = poly2ThicknessSlider.value();
let vertAmplitude = vertAmplitudeSlider.value();
let horizAmplitude = horizAmplitudeSlider.value();
let vertDensity = Math.floor(vertDensitySlider.value());
let horizDensity = Math.floor(horizDensitySlider.value());
let diagDensity = Math.floor(diagDensitySlider.value());
let dashLength = Math.floor(dashLengthSlider.value());
let dashGap = Math.floor(dashGapSlider.value());
let diagonalAngle = diagonalAngleSlider.value();
// Polygon 1 settings
let polyCount = Math.floor(polyCountSlider.value());
let polySides = Math.floor(polySidesSlider.value());
let polyRadius = polyRadiusSlider.value();
let polyDuplicates = Math.floor(polyDuplicatesSlider.value());
let polySpacing = polySpacingSlider.value();
// Polygon 2 settings
let poly2Sides = Math.floor(poly2SidesSlider.value());
let poly2Radius = poly2RadiusSlider.value();
let poly2Duplicates = Math.floor(poly2DuplicatesSlider.value());
let poly2Spacing = poly2SpacingSlider.value();
let offset = offsetSlider.value();
// Get values from toggles
let showVertical = verticalToggle.checked();
let showHorizontal = horizontalToggle.checked();
let showDiagonal = diagonalToggle.checked();
let showPolygon = polygonToggle.checked();
let showSecondPolygon = polygon2Toggle.checked();
let useRotatingPolygons = polyRotateToggle.checked();
// Draw layers in bottom-to-top order
// Layer 1 (bottom): Polygons (either concentric or rotating based on toggle)
if (showPolygon) {
if (useRotatingPolygons) {
drawAlternatingPolygons(
polygonColor, polygon2Color,
polyThickness, poly2Thickness,
polyCount,
polySides, poly2Sides,
polyRadius, poly2Radius,
polyDuplicates, poly2Duplicates,
polySpacing, poly2Spacing,
true, // rotating
showSecondPolygon
);
} else {
drawAlternatingPolygons(
polygonColor, polygon2Color,
polyThickness, poly2Thickness,
polyCount,
polySides, poly2Sides,
polyRadius, poly2Radius,
polyDuplicates, poly2Duplicates,
polySpacing, poly2Spacing,
false, // concentric
showSecondPolygon
);
}
}
// Layer 2: Diagonal dashed lines
if (showDiagonal) {
drawDiagonalDashedLines(diagonalColor, diagThickness, diagDensity, dashLength, dashGap, diagonalAngle);
}
// Layer 3: Vertical wavy lines
if (showVertical) {
drawVerticalWavyLines(primaryColor, vertThickness, vertAmplitude, vertDensity, offset);
}
// Layer 4 (top): Horizontal wavy lines
if (showHorizontal) {
drawHorizontalWavyLines(secondaryColor, horizThickness, horizAmplitude, horizDensity, offset);
}
}
function drawVerticalWavyLines(color, thickness, amplitude, density, offset) {
stroke(color);
strokeWeight(thickness);
let numLines = 40;
for (let i = 0; i < numLines; i++) {
let x = (i * width) / numLines;
beginShape();
for (let y = 0; y <= height; y++) {
let normalizedY = ((y % tileHeight) + tileHeight) % tileHeight / tileHeight;
let phase = TWO_PI * offset;
let lineIndex = Math.floor(i / 2);
let waveX = x + amplitude * sin(
TWO_PI * density * normalizedY +
phase * (lineIndex / (numLines/2))
);
vertex(waveX, y);
}
endShape();
}
}
function drawHorizontalWavyLines(color, thickness, amplitude, density, offset) {
stroke(color);
strokeWeight(thickness);
let numLines = 40;
for (let i = 0; i < numLines; i++) {
let y = (i * height) / numLines;
beginShape();
for (let x = 0; x <= width; x++) {
let normalizedX = ((x % tileWidth) + tileWidth) % tileWidth / tileWidth;
let phase = TWO_PI * offset;
let lineIndex = Math.floor(i / 2);
let waveY = y + amplitude * sin(
TWO_PI * density * normalizedX +
phase * (lineIndex / (numLines/2))
);
vertex(x, waveY);
}
endShape();
}
}
function drawDiagonalDashedLines(color, thickness, spacing, dashLength, dashGap, angle) {
stroke(color);
strokeWeight(thickness);
let angleRad = radians(angle);
let dx = cos(angleRad);
let dy = sin(angleRad);
let perpDx = -dy;
let perpDy = dx;
let diagonal = sqrt(pow(width, 2) + pow(height, 2));
let numLines = ceil(diagonal / spacing) * 2;
let patternLength = dashLength + dashGap;
let centerX = width / 2;
let centerY = height / 2;
for (let i = -numLines/2; i < numLines/2; i++) {
let perpOffset = i * spacing;
let startX = centerX + perpDx * perpOffset - dx * diagonal;
let startY = centerY + perpDy * perpOffset - dy * diagonal;
let lineLength = diagonal * 2;
let numPatterns = Math.max(1, Math.round(lineLength / patternLength));
let adjustedPatternLength = lineLength / numPatterns;
let adjustedDashLength = (dashLength / patternLength) * adjustedPatternLength;
for (let j = 0; j < numPatterns; j++) {
let dashStartDist = j * adjustedPatternLength;
let dashEndDist = dashStartDist + adjustedDashLength;
let dashStartX = startX + dx * dashStartDist;
let dashStartY = startY + dy * dashStartDist;
let dashEndX = startX + dx * dashEndDist;
let dashEndY = startY + dy * dashEndDist;
if (isVisibleOnCanvas(dashStartX, dashStartY) || isVisibleOnCanvas(dashEndX, dashEndY)) {
line(dashStartX, dashStartY, dashEndX, dashEndY);
}
}
}
}
function drawAlternatingPolygons(color1, color2, thickness1, thickness2, count,
sides1, sides2, radius1, radius2,
duplicates1, duplicates2, spacing1, spacing2,
isRotating, useSecondType) {
// Calculate grid for placing polygon sets
let cols = count;
let rows = count;
let cellWidth = tileWidth / cols;
let cellHeight = tileHeight / rows;
// Draw alternating polygons in a grid across all tiles
for (let tileX = 0; tileX < 2; tileX++) {
for (let tileY = 0; tileY < 2; tileY++) {
// Calculate tile offset
let tileOffsetX = tileX * tileWidth;
let tileOffsetY = tileY * tileHeight;
// Draw polygon sets within this tile
for (let col = 0; col < cols; col++) {
for (let row = 0; row < rows; row++) {
// Calculate center of each polygon set
let centerX = tileOffsetX + cellWidth * (col + 0.5);
let centerY = tileOffsetY + cellHeight * (row + 0.5);
// Determine which polygon type to draw based on checkerboard pattern
let isType1 = (col + row) % 2 === 0;
if (isType1 || !useSecondType) {
// Draw first polygon type
stroke(color1);
strokeWeight(thickness1);
if (isRotating) {
drawRotatingSet(centerX, centerY, radius1, sides1, duplicates1, spacing1);
} else {
drawConcentricSet(centerX, centerY, radius1, sides1, duplicates1, spacing1);
}
} else {
// Draw second polygon type
stroke(color2);
strokeWeight(thickness2);
if (isRotating) {
drawRotatingSet(centerX, centerY, radius2, sides2, duplicates2, spacing2);
} else {
drawConcentricSet(centerX, centerY, radius2, sides2, duplicates2, spacing2);
}
}
}
}
}
}
}
function drawConcentricSet(centerX, centerY, maxRadius, sides, duplicates, spacing) {
// Draw each concentric polygon, starting with the outermost
for (let i = 0; i < duplicates; i++) {
// Calculate radius for this polygon
// Start with the maximum radius and reduce by spacing for each inner polygon
let radius = maxRadius - (i * spacing);
// Only draw if radius is positive
if (radius > 0) {
// Draw a regular polygon with no rotation (0)
drawRegularPolygon(centerX, centerY, radius, sides, 0);
}
}
}
function drawRotatingSet(centerX, centerY, radius, sides, duplicates, rotationStep) {
// Draw each polygon with a different rotation
for (let i = 0; i < duplicates; i++) {
// Evenly distribute rotations
// Convert rotation step from degrees to radians
let stepRad = radians(rotationStep);
let rotation = i * stepRad;
// Draw a regular polygon with this rotation
drawRegularPolygon(centerX, centerY, radius, sides, rotation);
}
}
function drawRegularPolygon(centerX, centerY, radius, sides, rotation) {
beginShape();
for (let i = 0; i < sides; i++) {
let angle = rotation + TWO_PI * i / sides;
let x = centerX + cos(angle) * radius;
let y = centerY + sin(angle) * radius;
vertex(x, y);
}
endShape(CLOSE);
}
function isVisibleOnCanvas(x, y) {
return x >= 0 && x <= width && y >= 0 && y <= height;
}
function downloadSingleTile() {
let singleTileImage = get(0, 0, tileWidth, tileHeight);
let timestamp = year() + nf(month(), 2) + nf(day(), 2) + "_" + nf(hour(), 2) + nf(minute(), 2) + nf(second(), 2);
let saveCanvas = createGraphics(tileWidth, tileHeight);
saveCanvas.image(singleTileImage, 0, 0);
saveCanvas.save('guilloche_tile_' + timestamp + '.png');
saveCanvas.remove();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment