var colors = ["#FFC30F", "#581845", "#900C3F", "#C70039", "#FF5733", "#1AC7C4"];
SIZE = min(windowWidth, windowHeight);
createCanvas(windowWidth, windowHeight);
const SEGMENTS = random(5, 10);
var screenPct = min(height, width) / 1000;
INNER_RADIUS = screenPct / 5;
RADIUS_VARIATION = screenPct * 600;
for (var i = 0; i < SEGMENTS; i++) {
iniVertices.push(pointForIndex(i/SEGMENTS, width/2, height/2));
const valX = iniVertices.map(d => d.x);
const valY = iniVertices.map(d => d.y);
const minX = Math.min(...valX);
const maxX = Math.max(...valX);
const minY = Math.min(...valY);
const maxY = Math.max(...valY);
const centerX = (maxX - minX) / 2 + minX;
const centerY = (maxY - minY) / 2 + minY;
randomFill(centerX, centerY, 1);
var hobbyPoints = createHobbyBezier(iniVertices, { tension: 1, cyclic: true });
vertex(iniVertices[0].x, iniVertices[0].y);
hobbyPoints.forEach(({ startControl, endControl, point }) => {
bezierVertex(startControl.x, startControl.y, endControl.x, endControl.y, point.x, point.y);
let polygon = createPolygon(iniVertices);
const area = sqrt(pow(polygon.maxX - polygon.minX, 2) + pow(polygon.maxY - polygon.minY, 2));
for(var i = 1; i <= 3; i++){
drawingContext.fillStyle = "black";
randomFill(centerX, centerY, i%2==0?-i:i);
const vertices = getPaddingVertices(polygon, area / 30);
hobbyPoints = createHobbyBezier(vertices, { tension: 1, cyclic: true });
vertex(vertices[0].x, vertices[0].y);
hobbyPoints.forEach(({ startControl, endControl, point }) => {
bezierVertex(startControl.x, startControl.y, endControl.x, endControl.y, point.x, point.y);
polygon = createPolygon(vertices);
function randomFill(x, y, vel){
const ctx = drawingContext;
const gradient = ctx.createConicGradient(frameCount/20*vel, x, y);
const iniColor = random(colors);
const amount = floor(random(3, 6));
gradient.addColorStop(0, iniColor);
for(var i = 0; i < amount; i++){
gradient.addColorStop(1/(amount+2) * (i+1), random(colors));
gradient.addColorStop(1, iniColor);
ctx.fillStyle = gradient;
function createPolygon(vertices)
var polygon = {vertices: vertices};
var minX = (vertices.length > 0) ? vertices[0].x : undefined;
var minY = (vertices.length > 0) ? vertices[0].y : undefined;
for (var i = 0; i < polygon.vertices.length; i++) {
vertex2: vertices[(i + 1) % vertices.length],
edge.outwardNormal = outwardEdgeNormal(edge);
edge.inwardNormal = inwardEdgeNormal(edge);
minX = Math.min(x, minX);
minY = Math.min(y, minY);
maxX = Math.max(x, maxX);
maxY = Math.max(y, maxY);
function inwardEdgeNormal(edge)
var dx = edge.vertex2.x - edge.vertex1.x;
var dy = edge.vertex2.y - edge.vertex1.y;
var edgeLength = Math.sqrt(dx*dx + dy*dy);
return {x: -dy/edgeLength, y: dx/edgeLength};
function outwardEdgeNormal(edge)
var n = inwardEdgeNormal(edge);
return {x: -n.x, y: -n.y};
function getPaddingVertices(polygon, shapePadding)
for (var i = 0; i < polygon.edges.length; i++) {
var edge = polygon.edges[i];
var dx = edge.inwardNormal.x * shapePadding;
var dy = edge.inwardNormal.y * shapePadding;
offsetEdges.push(createOffsetEdge(edge, dx, dy));
for (var i = 0; i < offsetEdges.length; i++) {
var thisEdge = offsetEdges[i];
var prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length];
var vertex = edgesIntersection(prevEdge, thisEdge);
x: (prevEdge.vertex2.x + thisEdge.vertex1.x) / 2,
y: (prevEdge.vertex2.y + thisEdge.vertex1.y) / 2
function createOffsetEdge(edge, dx, dy)
vertex1: {x: edge.vertex1.x + dx, y: edge.vertex1.y + dy},
vertex2: {x: edge.vertex2.x + dx, y: edge.vertex2.y + dy}
function edgesIntersection(edgeA, edgeB)
var den = (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) - (edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y);
var ua = ((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / den;
var ub = ((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - (edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / den;
if (ua < 0 || ub < 0 || ua > 1 || ub > 1)
return {x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x), y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y)};
function appendArc(vertices, center, radius, startVertex, endVertex, isPaddingBoundary)
const twoPI = Math.PI * 2;
var startAngle = Math.atan2(startVertex.y - center.y, startVertex.x - center.x);
var endAngle = Math.atan2(endVertex.y - center.y, endVertex.x - center.x);
var angle = ((startAngle > endAngle) ? (startAngle - endAngle) : (startAngle + twoPI - endAngle));
var angle5 = ((isPaddingBoundary) ? -angle : twoPI - angle) / arcSegmentCount;
vertices.push(startVertex);
for (var i = 1; i < arcSegmentCount; ++i) {
var angle = startAngle + angle5 * i;
x: center.x + Math.cos(angle) * radius,
y: center.y + Math.sin(angle) * radius,
vertices.push(endVertex);
function pointForIndex(pct, x, y) {
let angle = pct * TWO_PI;
let cosAngle = cos(angle);
let sinAngle = sin(angle);
let time = frameCount / 300;
let noiseValue = noise(NOISE_SCALE * cosAngle + NOISE_SCALE, NOISE_SCALE * sinAngle + NOISE_SCALE, time);
let radius = INNER_RADIUS + RADIUS_VARIATION * noiseValue;
x: radius * cosAngle + x,