├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── favicon.ico ├── fractals.css ├── fractals.js ├── img ├── css-houdini-fractals.png ├── favicon.sketch ├── fractals-1.png ├── fractals-2.png └── fractals-3.png ├── index.html ├── package.json └── server.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Binary Files 5 | *.sketch binary 6 | *.ico binary 7 | *.png binary 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files 2 | .DS_Store 3 | Desktop.ini 4 | Thumbs.db -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS Houdini Fractals 2 | 3 | Drawing Fractals with CSS Houdini. 4 | 5 | This demo was created for https://github.com/GoogleChromeLabs/houdini.how and can be also previewed on the main Chrome Labs site https://houdini.how/ 6 | 7 | **From:** https://developer.mozilla.org/en-US/docs/Web/Houdini 8 | > Houdini is a set of low-level APIs that exposes parts of the CSS engine, giving developers the power to extend CSS by hooking into the styling and layout process of a browser’s rendering engine. 9 | 10 | ## 🌐 Try it online! 11 | 12 | https://conradsollitt.github.io/css-houdini-fractals/ 13 | 14 | ## 🚀 Download locally 15 | 16 | This repository has zero dependencies. Only the [fractals.js](fractals.js) file is required to use it with any site. If you would like to try the demo locally download this repository and follow the commands below. 17 | 18 | ```bash 19 | # If you have node installed: 20 | npm start 21 | 22 | # If you do not have node installed and have Python installed then depending on 23 | # the version Python and OS installed one of the following commands should work: 24 | python3 -m http.server 25 | python -m http.server 26 | python -m SimpleHTTPServer 27 | ``` 28 | 29 | CSS Houdini Fractals 30 | 31 | CSS Houdini Fractals 32 | 33 | CSS Houdini Fractals 34 | 35 | CSS Houdini Fractals 36 | 37 | ## 💡 Usage 38 | 39 | ### Use CSS Houdini API to import the [fractals.js](fractals.js) file 40 | 41 | ```js 42 | // Modern Browsers - Import JavaScript file from CDN 43 | if ('paintWorklet' in CSS) { 44 | CSS.paintWorklet.addModule('https://unpkg.com/css-houdini-fractals@1.1.0/fractals.js'); 45 | } 46 | 47 | // Modern Browsers - Using Local build, only a single file is needed 48 | if ('paintWorklet' in CSS) { 49 | CSS.paintWorklet.addModule('fractals.js'); 50 | } 51 | ``` 52 | 53 | ### Use the [css-paint-polyfill] Polyfill to support all Modern Browsers (won't work with IE) 54 | 55 | https://github.com/GoogleChromeLabs/css-paint-polyfill 56 | 57 | ```html 58 | 66 | ``` 67 | 68 | ### HTML 69 | 70 | The type of HTML element and class name does not matter, rather height and width are needed. 71 | 72 | ```html 73 | 76 |
77 |
78 | ``` 79 | 80 | ### CSS Options 81 | 82 | ```css 83 | /* 84 | By default only `background-image: paint(fractals)` is needed. 85 | */ 86 | .fractals { 87 | background-image: paint(fractals); 88 | } 89 | 90 | /* 91 | The example below shows all options with default values, except 92 | for [--colors] which is empty resulting in black lines. 93 | 94 | [--colors] are dynamic and based on the number of colors included. 95 | The delimiter for [--colors] is a space so these examples are all valid: 96 | --colors: red green blue; 97 | --colors: black; 98 | --colors: #000 #222 #444 #666 #888 #aaa #ccc; 99 | 100 | [--shape] = One of [line, circle, square] 101 | [--debug-to-console] and [-show-origin] = 0 or 1 102 | */ 103 | .fractals { 104 | --colors: red green blue cyan magenta yellow; 105 | --angle: 30; 106 | --starting-length-percent: 22; 107 | --next-line-size: 0.8; 108 | --shape: line; 109 | --max-draw-count: 10000; 110 | --debug-to-console: 0; 111 | --show-origin: 0; 112 | background-image: paint(fractals); 113 | } 114 | ``` 115 | 116 | ## 🎨 CSS Houdini Links 117 | 118 | * https://houdini.how/ 119 | * https://github.com/GoogleChromeLabs/houdini.how 120 | * https://developer.mozilla.org/en-US/docs/Web/Houdini 121 | 122 | ## ❄️ Fractals 123 | 124 | * https://fractalfoundation.org/resources/what-are-fractals/ 125 | * https://en.wikipedia.org/wiki/Fractal 126 | * https://mathworld.wolfram.com/Fractal.html 127 | * https://www.wired.com/2010/09/fractal-patterns-in-nature/ 128 | 129 | ## 🌐 Additional JavaScript Fractal Links 130 | * https://thefractal.zone/ 131 | * https://github.com/delimitry/fractals-js 132 | * https://progur.com/2017/02/create-mandelbrot-fractal-javascript.html 133 | * https://medium.com/@yortuc/fractal-fun-with-javascript-2102d03ad22b 134 | * https://progur.com/2016/10/procedural-generation-create-fractal-trees-javascript.html 135 | * https://lautarolobo.xyz/blog/use-javascript-and-html5-to-code-a-fractal-tree/ 136 | * https://www.lesscake.com/fractals-chaos-game 137 | * https://repl.it/talk/learn/Julia-Fractals-in-JavaScript/12806 138 | * http://rectangleworld.com/blog/archives/462 139 | * http://slicker.me/fractals/animate.htm 140 | 141 | ## 📝 License 142 | 143 | [![CC0](https://licensebuttons.net/p/zero/1.0/88x31.png)](https://creativecommons.org/publicdomain/zero/1.0/) 144 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConradSollitt/css-houdini-fractals/7361fca9b05a112a02f747f01af30b24dc46ff98/favicon.ico -------------------------------------------------------------------------------- /fractals.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | html { 6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 7 | } 8 | 9 | body { 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | background-image: linear-gradient(180deg, hsl(0 0% 100% / 0.2) 1%, hsl(0 0% 100% / 0.2) 30%,#fff), 14 | linear-gradient(25deg, #ce084b, #017bdc 32%,#FFEB3B); 15 | background-repeat: no-repeat; 16 | background-size: cover; 17 | } 18 | 19 | h1, 20 | section { 21 | padding: 20px; 22 | margin: 20px; 23 | background-color: white; 24 | box-shadow: 0 1px 2px rgba(0,0,0,.5); 25 | } 26 | section { max-width: calc(100vw - 80px); } 27 | 28 | /* 29 | For more on how the

works see my personal site and the CSS source: 30 | https://conradsollitt.com/ 31 | */ 32 | h1 { font-size:3em; letter-spacing: 0.08em; text-align: center; } 33 | h1 span { --h: 0; --s: 100%; --l: 50%; } 34 | h1 span:nth-child(2n) { --h: 120; } 35 | h1 span:nth-child(3n) { --h: 240; } 36 | h1 span:nth-child(4n) { --h: 60; } 37 | h1 span { 38 | color: hsl(var(--h), var(--s), var(--l)); 39 | --darkest: calc(var(--l) - 30%); 40 | --outline: hsl(var(--h), var(--s), var(--darkest)); 41 | text-shadow: 42 | -1px -1px var(--outline), 43 | 1px -1px var(--outline), 44 | -1px 1px var(--outline), 45 | 1px 1px var(--outline); 46 | } 47 | 48 | .flex { display:flex; flex-wrap: wrap; justify-content: center; } 49 | .center { justify-content: center; } 50 | 51 | section > div { 52 | display: flex; 53 | padding: 10px; 54 | flex-direction: column; 55 | } 56 | label { 57 | margin-bottom: 8px; 58 | width:150px; 59 | color: #888; 60 | } 61 | select, 62 | input[type="text"], 63 | input[type="number"] { padding:.5em 1em; } 64 | input { position: relative; } 65 | input::before { 66 | position: absolute; 67 | content: attr(current-value); 68 | top: -25px; 69 | right: 0; 70 | font-weight: bold; 71 | font-size: 1.2em; 72 | color: black; 73 | } 74 | @media (min-width: 800px) { 75 | section > div { flex-direction: row; justify-content: space-between; } 76 | label { margin-bottom: 0; margin-right: 60px; } 77 | input[type="range"] { width: 200px; } 78 | input::before { left: -40px; right: initial; top: initial; } 79 | input#max-draw-count::before { left: -60px; } 80 | } 81 | 82 | .demo { 83 | width: calc(100vw - 80px); 84 | height: 50vh; 85 | } 86 | @media (min-width: 800px) { 87 | .demo { width:600px; } 88 | } 89 | @media (min-width: 1200px) { 90 | .demo { width:50vw; } 91 | } 92 | 93 | a[href^="https://github.com/"] { 94 | display: flex; 95 | align-items: center; 96 | justify-content: space-between; 97 | margin-top: 2em; 98 | } 99 | a[href^="https://github.com/"]::before { 100 | content: ''; 101 | background-image: url(https://github.githubassets.com/favicons/favicon.svg); 102 | background-repeat: no-repeat; 103 | height: 32px; 104 | width: 32px; 105 | padding-right: 2em; 106 | } 107 | 108 | /* 109 | By default only `background-image: paint(fractals)` is needed. 110 | 111 | The example below shows all options with default values, except 112 | for [--colors] which is empty resulting in black lines. 113 | 114 | [--colors] are dynamic and based on the number of colors included. 115 | [--shape] = One of [line, circle, square] 116 | [--debug-to-console] and [-show-origin] = 0 or 1 117 | */ 118 | .fractals { 119 | --colors: red green blue cyan magenta yellow; 120 | --angle: 30; 121 | --starting-length-percent: 22; 122 | --next-line-size: 0.8; 123 | --shape: line; 124 | --max-draw-count: 10000; 125 | --debug-to-console: 0; 126 | --show-origin: 0; 127 | background-image: paint(fractals); 128 | } 129 | -------------------------------------------------------------------------------- /fractals.js: -------------------------------------------------------------------------------- 1 | /* global registerPaint */ 2 | /* jshint esversion:6 */ 3 | 4 | /** 5 | * Fractal Drawing with CSS Houdini 6 | * 7 | * @link https://houdini.how/ 8 | * @link https://github.com/GoogleChromeLabs/houdini.how 9 | * @link https://developer.mozilla.org/en-US/docs/Web/Houdini 10 | * @link https://fractalfoundation.org/resources/what-are-fractals/ 11 | * @link https://en.wikipedia.org/wiki/ 12 | * @link https://mathworld.wolfram.com/Fractal.html 13 | * @link https://www.wired.com/2010/09/fractal-patterns-in-nature/ 14 | * @author Conrad Sollitt (https://conradsollitt.com) 15 | * @license CC0 "Public Domain" license 16 | */ 17 | class Fractals { 18 | /** 19 | * Required Houdini API for Custom Properties. 20 | * If a variable is not included then `props.get(name)` will 21 | * return `null` from the `paint()` function. 22 | */ 23 | static get inputProperties() { 24 | return [ 25 | '--colors', 26 | '--angle', 27 | '--starting-length-percent', 28 | '--next-line-size', 29 | '--shape', 30 | '--max-draw-count', 31 | '--debug-to-console', 32 | '--show-origin', 33 | ]; 34 | } 35 | 36 | /** 37 | * Houdini API 38 | * 39 | * @param {PaintRenderingContext2D} ctx 40 | * @param {object} size 41 | * @param {object} props 42 | */ 43 | paint(ctx, size, props) { 44 | // Optional debug options 45 | const debugToConsole = (props.get('--debug-to-console').toString().trim() === '1'); 46 | let startDate; 47 | if (debugToConsole) { 48 | startDate = new Date(); 49 | console.log('-'.repeat(80)); 50 | console.log(`Starting CSS [paint(fractals)] at: ${startDate.toLocaleTimeString()}`); 51 | console.log(size); 52 | console.log(props); 53 | } 54 | this.showOrigin = (props.get('--show-origin').toString().trim() === '1'); 55 | 56 | // Default angle to 30% 57 | this.angle = parseInt(props.get('--angle'), 10) || 30; 58 | 59 | // Starting Line Length - Default to 22 % 60 | let startingLengthPct = parseInt(props.get('--starting-length-percent'), 10); 61 | if (isNaN(startingLengthPct) || startingLengthPct < 5 || startingLengthPct > 95) { 62 | startingLengthPct = 22; 63 | } 64 | const length = (size.height / (100 / startingLengthPct)); 65 | 66 | // Next line size - defaults to 0.8 for 80% 67 | this.nextLineSize = parseFloat(props.get('--next-line-size')); 68 | if (isNaN(this.nextLineSize) || this.nextLineSize < 0.1 || this.nextLineSize > 0.9) { 69 | this.nextLineSize = 0.8; 70 | } 71 | 72 | // For safety due to recursive function or by design if the number 73 | // is small only half (give or take) the the drawing will render. 74 | // If [--max-draw-count] is reached the number of shapes will be 75 | // slightly lager than the max draw count due to recursive functions 76 | // that need to finish running. 77 | this.maxDrawCount = parseInt(props.get('--max-draw-count'), 10) || 10000; 78 | this.drawCount = 0; 79 | 80 | // Colors are space delimited 81 | this.colors = props.get('--colors').toString().trim().split(' ').map(s => s.trim()); 82 | this.colorCount = this.colors.length; 83 | 84 | // Function for type Shape to Draw - Defaults to line. 85 | let shape; 86 | const x = (size.width / 2); 87 | const y = size.height - (this.showOrigin ? 4 : 0); 88 | switch (props.get('--shape').toString().trim()) { 89 | case 'circle': 90 | shape = (ctx, diameter) => { 91 | const radius = (diameter / 2); 92 | ctx.arc(0, -radius, radius, 0, Math.PI * 2, true); 93 | }; 94 | break; 95 | case 'square': 96 | shape = (ctx, length) => { 97 | // Typically when using canvas a square would be drawn using 98 | // `rect()`, however due to the rotated origin each line of 99 | // the square must be drawn to match the layout of other shapes. 100 | // ctx.rect(x, y, length, length); 101 | const half_len = length / 2; 102 | ctx.moveTo(0, 0); 103 | ctx.lineTo(half_len, 0); 104 | ctx.lineTo(half_len, -length); 105 | ctx.lineTo(0, -length); 106 | ctx.lineTo(-half_len, -length); 107 | ctx.lineTo(-half_len, 0); 108 | ctx.lineTo(0, 0); 109 | }; 110 | break; 111 | default: // 'line' 112 | shape = (ctx, length) => { 113 | ctx.moveTo(0, 0); 114 | ctx.lineTo(0, -length); 115 | }; 116 | } 117 | 118 | // Start call to recursive `draw()` 119 | this.draw(ctx, shape, x, y, length, 0, 0); 120 | if (debugToConsole) { 121 | const endDate = new Date(); 122 | const duration = endDate.getTime() - startDate.getTime(); 123 | console.log(`Finished CSS [paint(fractals)] at: ${startDate.toLocaleTimeString()}`); 124 | console.log(`Duration in milliseconds: ${duration}`); 125 | console.log(`Function calls: ${this.drawCount}`); 126 | } 127 | } 128 | 129 | /** 130 | * Recursive function that draws the fractals 131 | * 132 | * @param {PaintRenderingContext2D} ctx 133 | * @param {function} shape 134 | * @param {number} x 135 | * @param {number} y 136 | * @param {number} length 137 | * @param {number} angle 138 | */ 139 | draw(ctx, shape, x, y, length, angle) { 140 | // Draw Shape 141 | ctx.beginPath(); 142 | ctx.save(); 143 | ctx.translate(x, y); 144 | ctx.rotate(angle * Math.PI / 180); 145 | if (this.colorCount > 0) { 146 | const colorIndex = this.drawCount % this.colorCount; 147 | ctx.strokeStyle = this.colors[colorIndex]; 148 | } 149 | shape(ctx, length); 150 | ctx.stroke(); 151 | 152 | // This helps with debugging when adding new shapes 153 | if (this.showOrigin) { 154 | ctx.fillStyle = ctx.strokeStyle; 155 | ctx.fillRect(0, 0, 4, 4); 156 | } 157 | 158 | // Exit once the line length becomes too small or 159 | // if the number of max function calls is exceeded. 160 | this.drawCount++; 161 | if (this.drawCount > this.maxDrawCount || length < 10) { 162 | ctx.restore(); 163 | return; 164 | } 165 | 166 | // Recursively call this function twice, once for the left side and once for the right side 167 | this.draw(ctx, shape, 0, -length, length * this.nextLineSize, -this.angle); 168 | this.draw(ctx, shape, 0, -length, length * this.nextLineSize, this.angle); 169 | ctx.restore(); 170 | } 171 | } 172 | 173 | registerPaint('fractals', Fractals); 174 | -------------------------------------------------------------------------------- /img/css-houdini-fractals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConradSollitt/css-houdini-fractals/7361fca9b05a112a02f747f01af30b24dc46ff98/img/css-houdini-fractals.png -------------------------------------------------------------------------------- /img/favicon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConradSollitt/css-houdini-fractals/7361fca9b05a112a02f747f01af30b24dc46ff98/img/favicon.sketch -------------------------------------------------------------------------------- /img/fractals-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConradSollitt/css-houdini-fractals/7361fca9b05a112a02f747f01af30b24dc46ff98/img/fractals-1.png -------------------------------------------------------------------------------- /img/fractals-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConradSollitt/css-houdini-fractals/7361fca9b05a112a02f747f01af30b24dc46ff98/img/fractals-2.png -------------------------------------------------------------------------------- /img/fractals-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConradSollitt/css-houdini-fractals/7361fca9b05a112a02f747f01af30b24dc46ff98/img/fractals-3.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CSS Houdini Fractals Demo 7 | 8 | 9 | 10 | 11 |

CSS Houdini Fractals Demo

12 | 13 |
14 |
15 |
16 |
17 | 18 | 24 |
25 |
26 | 27 | 32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 | 43 | 44 |
45 |
46 | 47 | 48 |
49 |
50 | 51 | 55 |
56 |
57 | 58 | 62 |
63 | 66 |
67 |
68 | 69 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-houdini-fractals", 3 | "version": "1.1.0", 4 | "description": "Fractals Drawing with CSS Houdini", 5 | "main": "fractals.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ConradSollitt/css-houdini-fractals.git" 13 | }, 14 | "keywords": [ 15 | "CSS", 16 | "Houdini", 17 | "Fractals" 18 | ], 19 | "author": "Conrad Sollitt (https://conradsollitt.com)", 20 | "license": "CC0-1.0", 21 | "homepage": "https://github.com/ConradSollitt/css-houdini-fractals" 22 | } 23 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple Node Web Server for sending static files using using only 3 | * Node.js built-in features without any outside dependencies. 4 | * 5 | * To run: 6 | * npm start 7 | * 8 | * If you do not have node/npm installed and still want to run this 9 | * site locally then you can also run it if you have Python installed. 10 | * Depending on the version Python installed and the OS one of the 11 | * following commands should work: 12 | * python3 -m http.server 13 | * python -m http.server 14 | * python -m SimpleHTTPServer 15 | * 16 | * Important - This script is very minimal by design and includes no error handling. 17 | * It works well but has few features so is not intended for production apps. 18 | * 19 | * Since this file is small you can step through the code using a debugger 20 | * which makes it is useful for learning purposes. 21 | * 22 | * @link https://github.com/dataformsjs/awesome-web-react/blob/master/server.js 23 | * @author Conrad Sollitt (https://conradsollitt.com) 24 | * @license CC0 "Public Domain" license 25 | */ 26 | 27 | /* Validates online with both [jshint] and [eslint] */ 28 | /* jshint esversion:8, node:true */ 29 | /* eslint-env node, es6 */ 30 | 31 | 'use strict'; 32 | 33 | const http = require('http'); 34 | const url = require('url'); 35 | const fs = require('fs'); 36 | const path = require('path'); 37 | 38 | /** 39 | * Allowed mime types 40 | */ 41 | const mimeTypes = { 42 | htm: 'text/html; charset=UTF-8', 43 | html: 'text/html; charset=UTF-8', 44 | css: 'text/css', 45 | txt: 'text/plain', 46 | js: 'application/javascript', 47 | jsx: 'application/jsx', 48 | json: 'application/json', 49 | graphql: 'application/graphql', 50 | png: 'image/png', 51 | jpg: 'image/jpeg', 52 | svg: 'image/svg+xml', 53 | ico: 'image/x-icon', 54 | }; 55 | 56 | /** 57 | * Simple Web Server for sending Static Files 58 | */ 59 | const server = http.createServer((req, res) => { 60 | // Send a 404 Page as an HTML Response 61 | function send404() { 62 | res.statusCode = 404; 63 | res.setHeader('Content-Type', 'text/html; charset=UTF-8'); 64 | res.end('

File not found

'); 65 | } 66 | 67 | // Send a Response from a File. If the file doesn't exist or the file type is 68 | // not defined in mimeTypes then a 404 HTML Response will be sent to the client. 69 | function sendFile(filePath) { 70 | const data = filePath.split('.'); 71 | const fileType = data[data.length-1]; 72 | if (mimeTypes[fileType] === undefined) { 73 | send404(); // File type not supported 74 | return; 75 | } 76 | fs.readFile(filePath, (err, content) => { 77 | if (err && err.code === 'ENOENT') { 78 | send404(); // File not found 79 | } else if (err) { 80 | console.error(err); 81 | res.statusCode = 500; 82 | res.end('

An error has occurred, check console

'); 83 | } else { 84 | res.setHeader('Content-Type', mimeTypes[fileType]); 85 | res.end(content); 86 | } 87 | }); 88 | } 89 | 90 | // Only allow GET Requests 91 | if (req.method !== 'GET') { 92 | res.statusCode = 405; 93 | res.setHeader('Content-Type', 'text/html; charset=UTF-8'); 94 | res.end('

Method not allowed

'); 95 | return; 96 | } 97 | 98 | // Send root [index.html] file 99 | const reqPath = url.parse(req.url).pathname; 100 | if (reqPath === '/') { 101 | sendFile(path.join(__dirname, 'index.html')); 102 | return; 103 | } 104 | 105 | // Does the request path match a under the scripts directory? If not send a 404. 106 | // A security check against Path Traversal Attacks in first performed in case 107 | // this script ends up being used on a production server. 108 | if (reqPath.includes('../') || reqPath.includes('..\\')) { 109 | send404(); 110 | return; 111 | } 112 | const filePath = path.join(__dirname, reqPath); 113 | sendFile(filePath); 114 | }); 115 | 116 | /** 117 | * Start the Server 118 | */ 119 | const port = 3000; 120 | server.listen(port, null, () => { 121 | console.log(`Server running at http://127.0.0.1:${port}/`); 122 | }); 123 | --------------------------------------------------------------------------------