`);\n\t\tsteps.push(``);\n\t});\n\n\treturn steps.join('');\n}\n\n\/**\n * Shuffle an array in place\n * @param {Array} a Array to shuffle\n * @return {Array} Shuffled array\n *\/\nfunction shuffle(a) {\n var j, x, i;\n for (i = a.length - 1; i > 0; i--) {\n j = Math.floor(Math.random() * (i + 1));\n x = a[i];\n a[i] = a[j];\n a[j] = x;\n }\n return a;\n}\n\n\/**\n * The star of the show\n *\/\nfunction generateTychoArt() {\n\t\/\/ Clear the contents of the SVG\n\twhile (svg.firstChild) { svg.removeChild(svg.firstChild); }\n\t\n\t\/\/ Define the poster size\n\tconst w = 1000;\n\tconst ratio = randomBool() ? 4\/3 : 1; \/\/ Sometimes a square, sometimes a rectangle\n\tconst h = w * ratio;\n\tsvg.setAttribute('width', w);\n\tsvg.setAttribute('height', h);\n\tsvg.setAttribute('viewBox', `0 0 ${w} ${h}`);\n\t\n\t\/\/ Set a background colour\n\tconst hasHalfBg = randomBool();\n\tconst hasGradientBg = randomBool();\n\tconst bgIndex = randomIndex(palette);\n\tconst bgIndex2 = getDifferentItem(palette, bgIndex, 1);\n\tconst bgColor = palette[ bgIndex ];\n\tconst bgColor2 = palette[ bgIndex2 ];\n\tconst bgImage = (hasGradientBg) ? `${bgColor} linear-gradient(to bottom, ${bgColor} 0%, ${bgColor2} ${hasHalfBg ? '50%' : '100%'})` : bgColor; \/\/ Use a CSS gradient so we don't need to define an SVG gradient and all that…\n\tsvg.style.setProperty('--bg', bgImage);\n\t\n\t\/\/ Define lists\n\tlet shapes = [];\n\tlet usedColors = [bgIndex];\n\t\n\t\/\/ Set up defs element, we'll add our gradients to this\n\tconst defs = document.createElementNS(ns, 'defs');\n\tsvg.appendChild(defs);\n\t\n\t\/\/ Define texture: https:\/\/fffuel.co\/gggrain\/\n\tdefs.innerHTML = `\n <\/feTurbulence>\n <\/feColorMatrix>\n \n <\/feFuncR>\n <\/feFuncG>\n <\/feFuncB>\n <\/feComponentTransfer>\n <\/feColorMatrix>\n <\/filter>`;\n\t\n\tconst rectColorIndex = getDifferentItem(palette, bgIndex, 2, [bgIndex2]);\n\tconst rectColor = palette[ rectColorIndex ];\n\t\n\t\/\/ If there is a half-rectangle, create the element and give it the colour above\n\tif (hasHalfBg) {\n\t\tconst rectBg = document.createElementNS(ns, 'rect');\n\t\trectBg.setAttribute('width', '100%');\n\t\trectBg.setAttribute('height', '50%');\n\t\trectBg.setAttribute('x', 0);\n\t\trectBg.setAttribute('y', '50%');\n\t\trectBg.setAttribute('fill', rectColor);\n\t\tsvg.appendChild(rectBg);\n\t\t\n\t\tusedColors.push(rectColorIndex);\n\t}\n\t\n\t\/* TRIANGLE *\/\n\tconst triangleColorIndex = getDifferentItem(palette, bgIndex, 4, usedColors);\n\tconst triangleColorIndex2 = getDifferentItem(palette, triangleColorIndex, 1);\n\tconst triangleColor = palette[ triangleColorIndex ];\n\tconst triangleColor2 = palette[ triangleColorIndex2 ];\n\tconst triangleColorGradientSmooth = `\n\t\t\t`;\n\tconst triangleColorStepGradientList = colorBlender(triangleColor, triangleColor2, 3);\n\tconst triangleColorStepGradient = buildSvgSteppedGradientStops(triangleColor,triangleColorStepGradientList,triangleColor2);\n\tconst isTriangleGradientStepped = randomBool();\n\tconst triangleColorGradient = `\n\t\t\t${(isTriangleGradientStepped ? triangleColorStepGradient : triangleColorGradientSmooth)}\n\t\t<\/linearGradient>`;\n\tdefs.innerHTML += triangleColorGradient;\n\tconst isTriangleGradient = (randomBool());\n\tconst triangleFill = (isTriangleGradient) ? 'url(#triangleGradient)' : triangleColor;\n\tconst triangle = document.createElementNS(ns, 'polygon');\n\tconst isTriangleEqui = randomBool();\n\tconst triangleWidth = (isTriangleEqui ? (Math.random() * (w\/2)) : Math.random() * w) + w\/2;\n\tconst triangleHeightMin = h\/3;\n\tconst triangleHeightMax = (triangleWidth * Math.sqrt(3)) \/ 2; \/\/ Equilateral triangle\n\tconst triangleHeight = isTriangleEqui ? triangleHeightMax : triangleHeightMin;\n\tconst triangleCenterOfMass = (triangleWidth * Math.sqrt(3)) \/ 3; \/\/ Distance from side for an equilateral triangle\n\tconst triangleCenterFromBottom = h\/2 - triangleCenterOfMass; \/\/ Triangle would be visually centered if the center of mass aligned to the middle of the canvas\n\tconst trianglePullUp = (isTriangleEqui && randomBool()) ? triangleCenterFromBottom : 0;\n\tconst triangleTopPointPos = h - triangleHeight - trianglePullUp;\n\ttriangle.setAttribute('points', `${w\/2},${ triangleTopPointPos } ${(w\/2 + triangleWidth\/2)},${h - trianglePullUp} ${(w\/2 - triangleWidth\/2)},${h - trianglePullUp}`);\n\ttriangle.setAttribute('fill', triangleFill);\n\tshapes.push(triangle);\n\tusedColors.push(triangleColorIndex);\n\t\n\t\/* CIRCLE *\/\n\tlet circleExcludeColors = Array.from(usedColors);\n\tif (hasHalfBg && rectColorIndex) { circleExcludeColors.push(rectColorIndex); }\n\tconst circleColorIndex = getDifferentItem(palette, triangleColorIndex, 2, circleExcludeColors);\n\tconst circleColorIndex2 = getDifferentItem(palette, circleColorIndex, 1, circleExcludeColors);\n\tconst circleColor = palette[ circleColorIndex ];\n\t\/\/ const circleColor2 = palette[ circleColorIndex2 ];\n\tconst circleColor2 = palette[ bgIndex ]; \/\/ Fade the circle into the background\n\tconst allowCircleGradient = (!isTriangleGradientStepped || !isTriangleGradient); \/\/ Don't want both the triangle and the circle to have a gradient\n\tconst isCircleGradient = (!hasGradientBg && randomBool() && allowCircleGradient);\n\tconst isCircleGradientSmooth = (!isTriangleGradientStepped && !hasHalfBg && randomBool()); \/\/ Two stepped gradients would be a bit much\n\tconst circleFill = (isCircleGradient) ? 'url(#circleGradient)' : circleColor;\n\tconst circle = document.createElementNS(ns, 'circle');\n\tconst circleRadius = Math.min(Math.max(0.25, Math.random()) * (w\/3), triangleHeight);\n\tlet circlePosY = remapNumber(round(Math.random(), 1), 0, 1, 0, h - triangleHeight);\n\tif (Math.abs(circlePosY - h\/2) < h\/10) { circlePosY = h\/2; } \/\/ Snap the circle to the center if the position is within 10% of it\n\tconst isCircleGradientOffset = randomBool();\n\tconst circleColorGradientSmooth = `\n\t\t\t\n\t\t\t\n\t\t<\/radialGradient>`;\n\tconst circleColorStepGradientList = colorBlender(circleColor, circleColor2, 3);\n\tconst circleColorStepGradient = `\n\t\t\t${ (buildSvgSteppedGradientStops(triangleColor,triangleColorStepGradientList,triangleColor2)) }\n\t\t<\/linearGradient>`;\n\tconst circleColorGradient = isCircleGradientSmooth ? circleColorGradientSmooth : circleColorStepGradient;\n\tdefs.innerHTML += circleColorGradient;\n\tcircle.setAttribute('r', circleRadius);\n\tcircle.setAttribute('cx', w\/2);\n\tcircle.setAttribute('cy', circlePosY);\n\tcircle.setAttribute('fill', circleFill);\n\tcircle.setAttribute('transform', `rotate(${(!isCircleGradientSmooth && randomBool() ? 45 : 0) } ${w\/2} ${circlePosY})`);\n\tshapes.push(circle);\n\tusedColors.push(circleColorIndex);\n\n\t\/\/ Sometimes the triangle looks better layered under the circle…\n\tif (randomBool() && circlePosY < triangleTopPointPos) { \/\/ If the circle's centre is above the top point of the triangle…\n\t\tshapes.reverse();\n\t}\n\t\/\/ shuffle(shapes);\n\t\n\t\/\/ Append the shapes\n\tfor (let shape of shapes) {\n\t\tsvg.appendChild(shape);\n\t}\n\t\n\t\/\/ Add the title\n\tconst text = document.createElementNS(ns, 'text');\n\tlet textColorIndex = getDifferentItem(palette, bgIndex, 2, usedColors);\n\ttext.setAttribute('x', '50%');\n\ttext.setAttribute('y', round(Math.random(), 1) * 100 + 160);\n\ttext.setAttribute('text-anchor', 'middle');\n\ttext.setAttribute('font-size', 160);\n\ttext.setAttribute('font-family', 'Helvetica, sans-serif');\n\ttext.setAttribute('fill', palette[ textColorIndex ]);\n\ttext.setAttribute('textLength', remapNumber(Math.random(), 0, 1, w * 0.6, w * 0.9));\n\ttext.setAttribute('lengthAdjust', 'spacingAndGlyphs');\n\ttext.textContent = 'TYCHO';\n\ttext.classList.add('tycho-text');\n\tsvg.appendChild(text);\n\t\n\tconst noiseLayer = document.createElementNS(ns, 'rect');\n\tnoiseLayer.setAttribute('width', '100%');\n\tnoiseLayer.setAttribute('height', '100%');\n\tnoiseLayer.setAttribute('fill', 'transparent');\n\tnoiseLayer.setAttribute('filter', 'url(#gggrain-filter)');\n\tnoiseLayer.setAttribute('opacity', '0.05');\n\tnoiseLayer.style.mixBlendMod = 'overlay';\n\tsvg.appendChild(noiseLayer);\n}\n\ndocument.addEventListener('DOMContentLoaded', generateTychoArt);\ndocument.querySelector('button').addEventListener('click', generateTychoArt);\ndocument.querySelector('input[type=\"checkbox\"]').addEventListener('change', function (e) {\n\tlet show = e.target.checked;\n\tdocument.documentElement.classList.toggle('hide-text', !show);\n});"] Rank: 167. ➜ +144 ➜ Previous Next ➜ 🖼 Index CSS EXPLORER PURE CSS ART GALLERY Loading artwork... Your browser does not support iframes. Christopher Kirk-Nielsen chriskirknielsen.com Codepen Twitter Github Art Style inspired by https://iso50.com/, which usually has a "sunset" composition. *Note: You MAY NOT use this to make NTFs.* Codepen View Source Code in CSS ART EXPLORER