Beautiful generative art by Dan Gries, from his tutorial: Generative Art in HTML5 Canvas – Sweeping Fractal Lines. The base code is in no way mine, I am just playing/experimenting on top of his brilliant work.
Last active
November 17, 2024 04:40
-
-
Save sxywu/4996c518e789a3d58369881f18513c72 to your computer and use it in GitHub Desktop.
Morphing Curve: Radial Gradient
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>HTML5 Canvas</title> | |
<script type="text/javascript"> | |
window.addEventListener("load", windowLoadHandler, false); | |
function windowLoadHandler() { | |
canvasApp(); | |
} | |
function canvasApp() { | |
var displayCanvas = document.getElementById("displayCanvas"); | |
var context = displayCanvas.getContext("2d"); | |
var displayWidth = displayCanvas.width; | |
var displayHeight = displayCanvas.height; | |
var numCircles; | |
var maxMaxRad; | |
var minMaxRad; | |
var minRadFactor; | |
var circles; | |
var iterations; | |
var timer; | |
var drawsPerFrame; | |
var drawCount; | |
var bgColor,urlColor; | |
var TWO_PI = 2*Math.PI; | |
var lineWidth; | |
init(); | |
function init() { | |
/* | |
In other experiments, you may wish to use more fractal curves ("circles") | |
and allow the radius of them to vary. If so, modify the next three variables. | |
*/ | |
numCircles = 2; | |
maxMaxRad = 100; | |
minMaxRad = 1; | |
/* | |
We draw closed curves with varying radius. The factor below should be set between 0 and 1, | |
representing the size of the smallest radius with respect to the largest possible. | |
*/ | |
minRadFactor = 0; | |
/* | |
The number of subdividing steps to take when creating a single fractal curve. | |
Can use more, but anything over 10 (thus 1024 points) is overkill for a moderately sized canvas. | |
*/ | |
iterations = 5; | |
//number of curves to draw on every tick of the timer | |
drawsPerFrame = 20; | |
bgColor = "#FFFFFF"; | |
urlColor = "#EEEEEE"; | |
lineWidth = 1; | |
startGenerate(); | |
} | |
function startGenerate() { | |
drawCount = 0; | |
context.setTransform(1,0,0,1,0,0); | |
context.clearRect(0,0,displayWidth,displayHeight); | |
setCircles(); | |
// onTimer(); | |
if(timer) {clearInterval(timer);} | |
timer = setInterval(onTimer,1000/50); | |
} | |
function setCircles() { | |
var i; | |
var r,g,b,a; | |
var maxR, minR; | |
var grad; | |
circles = []; | |
for (i = 0; i < numCircles; i++) { | |
maxR = maxMaxRad; | |
minR = 0; | |
//define gradient | |
grad = context.createRadialGradient(0,0,minR,0,0,maxR); | |
grad.addColorStop(1,"rgba(255,0,0,0.2)"); | |
grad.addColorStop(0,"rgba(255,255,0,0.2)"); | |
var newCircle = { | |
centerX: -maxR, | |
centerY: displayHeight/2-50, | |
maxRad : maxR, | |
minRad : minR, | |
color: grad, //can set a gradient or solid color here. | |
//fillColor: "rgba(0,0,0,1)", | |
param : 0, | |
changeSpeed : 1/250, | |
phase : TWO_PI, //the phase to use for a single fractal curve. | |
globalPhase: TWO_PI //the curve as a whole will rise and fall by a sinusoid. | |
}; | |
circles.push(newCircle); | |
newCircle.pointList1 = setLinePoints(iterations); | |
newCircle.pointList2 = setLinePoints(iterations); | |
} | |
} | |
function onTimer() { | |
var i,j; | |
var c; | |
var rad; | |
var point1,point2; | |
var x0,y0; | |
var cosParam; | |
var xSqueeze = 0.75; //cheap 3D effect by shortening in x direction. | |
var yOffset; | |
//draw circles | |
for (j = 0; j < drawsPerFrame; j++) { | |
drawCount++; | |
for (i = 0; i < numCircles; i++) { | |
c = circles[i]; | |
c.param += c.changeSpeed; | |
if (c.param >= 1) { | |
c.param = 0; | |
c.pointList1 = c.pointList2; | |
c.pointList2 = setLinePoints(iterations); | |
} | |
cosParam = 0.5-0.5*Math.cos(Math.PI*c.param); | |
context.strokeStyle = c.color; | |
context.lineWidth = lineWidth; | |
//context.fillStyle = c.fillColor; | |
context.beginPath(); | |
point1 = c.pointList1.first; | |
point2 = c.pointList2.first; | |
//slowly rotate | |
c.phase += 0.0002; | |
theta = c.phase; | |
rad = c.minRad + (point1.y + cosParam*(point2.y-point1.y))*(c.maxRad - c.minRad); | |
//move center | |
c.centerX += 0.5; | |
c.centerY += 0.04; | |
yOffset = 40*Math.sin(c.globalPhase + drawCount/1000*TWO_PI); | |
//stop when off screen | |
if (c.centerX > displayWidth + maxMaxRad) { | |
clearInterval(timer); | |
timer = null; | |
} | |
//we are drawing in new position by applying a transform. We are doing this so the gradient will move with the drawing. | |
context.setTransform(xSqueeze,0,0,1,c.centerX,c.centerY+yOffset) | |
//Drawing the curve involves stepping through a linked list of points defined by a fractal subdivision process. | |
//It is like drawing a circle, except with varying radius. | |
x0 = xSqueeze*rad*Math.cos(theta); | |
y0 = rad*Math.sin(theta); | |
context.lineTo(x0, y0); | |
while (point1.next != null) { | |
point1 = point1.next; | |
point2 = point2.next; | |
theta = TWO_PI*(point1.x + cosParam*(point2.x-point1.x)) + c.phase; | |
rad = c.minRad + (point1.y + cosParam*(point2.y-point1.y))*(c.maxRad - c.minRad); | |
x0 = xSqueeze*rad*Math.cos(theta); | |
y0 = rad*Math.sin(theta); | |
context.lineTo(x0, y0); | |
} | |
context.closePath(); | |
context.stroke(); | |
//context.fill(); | |
} | |
} | |
} | |
//Here is the function that defines a noisy (but not wildly varying) data set which we will use to draw the curves. | |
function setLinePoints(iterations) { | |
var pointList = {}; | |
pointList.first = {x:0, y:1}; | |
var lastPoint = {x:1, y:1} | |
var minY = 1; | |
var maxY = 1; | |
var point; | |
var nextPoint; | |
var dx, newX, newY; | |
var ratio; | |
var minRatio = 0.5; | |
pointList.first.next = lastPoint; | |
for (var i = 0; i < iterations; i++) { | |
point = pointList.first; | |
while (point.next != null) { | |
nextPoint = point.next; | |
dx = nextPoint.x - point.x; | |
newX = 0.5*(point.x + nextPoint.x); | |
newY = 0.5*(point.y + nextPoint.y); | |
newY += dx*(Math.random()*2 - 1); | |
var newPoint = {x:newX, y:newY}; | |
//min, max | |
if (newY < minY) { | |
minY = newY; | |
} | |
else if (newY > maxY) { | |
maxY = newY; | |
} | |
//put between points | |
newPoint.next = nextPoint; | |
point.next = newPoint; | |
point = nextPoint; | |
} | |
} | |
//normalize to values between 0 and 1 | |
if (maxY != minY) { | |
var normalizeRate = 1/(maxY - minY); | |
point = pointList.first; | |
while (point != null) { | |
point.y = normalizeRate*(point.y - minY); | |
point = point.next; | |
} | |
} | |
//unlikely that max = min, but could happen if using zero iterations. In this case, set all points equal to 1. | |
else { | |
point = pointList.first; | |
while (point != null) { | |
point.y = 1; | |
point = point.next; | |
} | |
} | |
return pointList; | |
} | |
} | |
</script> | |
<title>HTML5 Canvas Generative Art</title> | |
<style type="text/css"> | |
body {background-color:#ffffff; color:#333333;} | |
h4 {font-family: sans-serif; color:#333333; font-size:16px;} | |
h3 {font-family: sans-serif; color:#333333;} | |
p {font-family: sans-serif; color:#333333; font-size:14px;} | |
#caption {position:absolute; width:1024px; text-align:center; top:520px; z-index:1} | |
a {font-family: sans-serif; color:#d15423; text-decoration:none;} | |
canvas {} | |
#displayCanvas {position:absolute; top:10px; z-index:0;} | |
div {} | |
#container {width:1024px; height:576px; margin:auto;} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
<canvas id="displayCanvas" width="1024px" height="576px"> | |
Your browser does not support HTML5 canvas. | |
</canvas> | |
<form> | |
<p id="caption"> | |
HTML5 Canvas - Morphing Fractal Curve. | |
<br><a href="http://www.rectangleworld.com">rectangleworld.com</a> | |
</p> | |
</form> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment