<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<!DOCTYPE html><html lang='en' class=''>
<head><script src='//production-assets.codepen.io/assets/editor/live/console_runner-079c09a0e3b9ff743e39ee2d5637b9216b3545af0de366d4b9aad9dc87e26bfd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/events_runner-73716630c22bbc8cff4bd0f07b135f00a0bdc5d14629260c3ec49e5606f98fdd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/css_live_reload_init-2c0dc5167d60a5af3ee189d570b1835129687ea2a61bee3513dee3a50c115a77.js'></script><meta charset='UTF-8'><meta name="robots" content="noindex"><link rel="shortcut icon" type="image/x-icon" href="//production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" /><link rel="mask-icon" type="" href="//production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" /><link rel="canonical" href="https://codepen.io/RectangleWorld/pen/FlIky?limit=all&page=19&q=paint" />
<style class="cp-pen-styles">body {background-color:#000000; color:#333333;}
p {font-family: sans-serif; color:#333333; font-size:14px;}
a {font-family: sans-serif; color:#d15423; text-decoration:none;}
#caption {position:absolute; width:1024px; text-align:center; top:320px; z-index:1}
#displayCanvas {position:absolute; top:10px; z-index:0;}
#container {width:1024px; height:320px; margin:auto;}
</style></head><body>
<title>HTML5 Canvas Braids</title>
</head>
<body>
<div id="container">
<canvas id="displayCanvas" width="1024px" height="320px">
Your browser does not support HTML5 canvas.
</canvas>
<p id="caption">
HTML5 Canvas - Endless Braids. More info <a href="http://rectangleworld.com/blog/archives/733" target="_blank">here.</a><br><a href="http://www.rectangleworld.com">rectangleworld.com</a>
</p>
</div>
</body>
</html>
<script src='//production-assets.codepen.io/assets/common/stopExecutionOnTimeout-b2a7b3fe212eaa732349046d8416e00a9dec26eb7fd347590fbced3ab38af52e.js'></script>
<script >/*
Dan Gries
rectangleworld.com
See http://rectangleworld.com/blog/archives/733 for variations and more discussion of the code.
*/
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;
//off screen canvas
var bufferCanvas;
var bufferContext;
var rowHeight;
var stringSpacing;
var stringThickness;
var diskFactor;
var margin;
var bgColor;
var numStrings;
var crossingProbability;
var positiveProbability;
var crossingAngle;
var controlYFactor;
var spacerGap;
var generatorsInLastRow;
var colors;
var gradDX, gradDY;
var timer;
var scrollAmt;
init();
function init() {
rowHeight = 40;
stringSpacing = 25;
stringThickness = 9;
margin = stringThickness+1;
bgColor = "#000000";
numStrings = 1 + Math.floor((displayWidth-stringThickness)/stringSpacing);
margin = (displayWidth - (numStrings-1)*stringSpacing)/2;
crossingProbability = 0.67;
positiveProbability = 0.5;
spacerGap = 0.5;
crossingAngle = 42*Math.PI/180;
controlYFactor = (1 - stringSpacing/rowHeight*Math.tan(crossingAngle));
var gradDist = 2*stringThickness;
gradDX = gradDist*Math.cos(crossingAngle);
gradDY = gradDist*Math.sin(crossingAngle);
setInitialColors();
//initialize generatorsInLastRow - an array which records which braid generators appeared in the previous row.
//I want to know this in order to avoid a braid crossing followed by its inverse.
generatorsInLastRow = [];
for (var k = 0; k < numStrings-1; k++) {
generatorsInLastRow.push(0);
}
//set up buffer canvas which will hold next row to be added
bufferCanvas = document.createElement('canvas');
bufferCanvas.width = displayWidth;
bufferCanvas.height = rowHeight;
bufferContext = bufferCanvas.getContext("2d");
//paint initial screen with braids
context.fillStyle = bgColor;
context.fillRect(0,0,displayWidth,displayHeight);
var i = Math.ceil(displayHeight/rowHeight);
while (--i > -1) {
fillRow(context, i*rowHeight);
}
//fill buffer with next row
fillRow(bufferContext, 0);
scrollAmt = 1;
timer = setInterval(onTimer,1000/24);
}
function onTimer() {
//scroll down one pixel
context.drawImage(displayCanvas, 0, 0, displayWidth, displayHeight-1, 0, 1, displayWidth, displayHeight-1);
//clear top slice
context.fillStyle = bgColor;
context.fillRect(0,0,displayWidth,1);
//draw new top row when used up
if (scrollAmt > rowHeight) {
scrollAmt = 1;
bufferContext.clearRect(0,0,displayWidth,rowHeight);
fillRow(bufferContext, 0);
}
//copy from buffer into top slice
context.drawImage(bufferCanvas, 0, rowHeight-scrollAmt, displayWidth, 1, 0, 0, displayWidth, 1);
scrollAmt++;
}
function fillRow(ctx, y0) {
var stringNumber = 0;
var x0;
var temp;
var positiveSwitch;
var doPositive;
var prob = 0.5; //first crossing probability at 50%, rest will be set to desired crossingProbability set above.
while (stringNumber < numStrings - 1) {
x0 = margin + stringNumber*stringSpacing;
if (Math.random() < prob) {
positiveSwitch = (Math.random() < positiveProbability);
doPositive = (positiveSwitch && (generatorsInLastRow[stringNumber] != -1)) ||
((!positiveSwitch) && (generatorsInLastRow[stringNumber] == 1));
if (doPositive) {
drawCrossing(ctx, x0, y0, colors[stringNumber], colors[stringNumber+1], true);
generatorsInLastRow[stringNumber] = 1;
generatorsInLastRow[stringNumber+1] = 0;
}
else {
drawCrossing(ctx, x0, y0, colors[stringNumber], colors[stringNumber+1], false);
generatorsInLastRow[stringNumber] = -1;
generatorsInLastRow[stringNumber+1] = 0;
}
//permute colors
temp = colors[stringNumber];
colors[stringNumber] = colors[stringNumber+1];
colors[stringNumber+1] = temp;
//advance
stringNumber += 2;
}
else {
drawString(ctx, x0, y0, colors[stringNumber]);
stringNumber += 1;
}
}
if (stringNumber == numStrings - 1) {
drawString(ctx, margin + stringNumber*stringSpacing, y0, colors[stringNumber]);
}
//after first crossing probability of 50%, remaining crossing probabilities set to desired amount.
prob = crossingProbability;
}
function setInitialColors() {
var i;
var r,g,b;
var darkR, darkG, darkB;
var lightR, lightG, lightB;
var param;
colors = [];
var darkFactor = 0.33;
var lightAdd = 20;
for (i = 0; i < numStrings; i++) {
r = 64+Math.floor(Math.random()*180);
g = 64+Math.floor(Math.random()*180);
b = 64+Math.floor(Math.random()*180);
darkR = Math.floor(darkFactor*r);
darkG = Math.floor(darkFactor*g);
darkB = Math.floor(darkFactor*b);
lightR = Math.min(Math.floor(r + lightAdd),255);
lightG = Math.min(Math.floor(g + lightAdd),255);
lightB = Math.min(Math.floor(b + lightAdd),255);
var colorObj = {
base: "rgb("+r+","+g+","+b+")",
dark: "rgb("+darkR+","+darkG+","+darkB+")",
light: "rgb("+lightR+","+lightG+","+lightB+")"
}
colors.push(colorObj);
}
}
function drawString(ctx, x0,y0,color) {
ctx.strokeStyle = color.base;
ctx.lineWidth = stringThickness;
ctx.lineCap = "butt";
ctx.beginPath();
ctx.moveTo(x0,y0);
ctx.lineTo(x0,y0+rowHeight);
ctx.stroke();
}
function drawCrossing(ctx, x0,y0,color1,color2,positive) {
var grad;
var midX = x0 + stringSpacing/2;
var midY = y0 + rowHeight/2;
ctx.lineCap = "butt";
if (positive) {
grad = ctx.createLinearGradient(midX+gradDX, midY-gradDY, midX-gradDX, midY+gradDY);
grad.addColorStop(0, color1.base);
grad.addColorStop(0.5, color1.dark);
grad.addColorStop(1, color1.base);
ctx.strokeStyle = grad;
drawLine1();
grad = ctx.createLinearGradient(midX+gradDX, midY+gradDY, midX-gradDX, midY-gradDY);
grad.addColorStop(0, color2.base);
grad.addColorStop(0.5, color2.light);
grad.addColorStop(1, color2.base);
ctx.strokeStyle = grad;
drawLine2();
}
else {
grad = ctx.createLinearGradient(midX+gradDX, midY+gradDY, midX-gradDX, midY-gradDY);
grad.addColorStop(0, color2.base);
grad.addColorStop(0.5, color2.dark);
grad.addColorStop(1, color2.base);
ctx.strokeStyle = grad;
drawLine2();
grad = ctx.createLinearGradient(midX+gradDX, midY-gradDY, midX-gradDX, midY+gradDY);
grad.addColorStop(0, color1.base);
grad.addColorStop(0.5, color1.light);
grad.addColorStop(1, color1.base);
ctx.strokeStyle = grad;
drawLine1();
}
function drawLine1() {
ctx.lineWidth = stringThickness;
ctx.beginPath();
ctx.moveTo(x0+stringSpacing,y0);
ctx.bezierCurveTo(x0+stringSpacing, y0+rowHeight*controlYFactor,
x0, y0+rowHeight*(1-controlYFactor),
x0, y0+rowHeight);
ctx.stroke();
}
function drawLine2() {
ctx.lineWidth = stringThickness;
ctx.beginPath();
ctx.moveTo(x0,y0);
ctx.bezierCurveTo(x0, y0+rowHeight*controlYFactor,
x0+stringSpacing, y0+rowHeight*(1-controlYFactor),
x0+stringSpacing, y0+rowHeight);
ctx.stroke();
}
}
}
//# sourceURL=pen.js
</script>
</body></html>