<!doctype html><htmllang=endir=ltrclass="blog-wrapper blog-post-page plugin-blog plugin-id-default"data-has-hydrated=false><metacharset=UTF-8><metaname=generatorcontent="Docusaurus v3.6.1"><titledata-rh=true>Playing with fire: The fractal flame algorithm | The Old Speice Guy</title><metadata-rh=truename=viewportcontent="width=device-width,initial-scale=1.0"><metadata-rh=truename=twitter:cardcontent=summary_large_image><metadata-rh=trueproperty=og:urlcontent=https://speice.io/2024/11/playing-with-fire><metadata-rh=trueproperty=og:localecontent=en><metadata-rh=truename=docusaurus_localecontent=en><metadata-rh=truename=docusaurus_tagcontent=default><metadata-rh=truename=docsearch:languagecontent=en><metadata-rh=truename=docsearch:docusaurus_tagcontent=default><metadata-rh=trueproperty=og:titlecontent="Playing with fire: The fractal flame algorithm | The Old Speice Guy"><metadata-rh=truename=descriptioncontent="Wikipedia describes fractal flames as:"><metadata-rh=trueproperty=og:descriptioncontent="Wikipedia describes fractal flames as:"><metadata-rh=trueproperty=og:typecontent=article><metadata-rh=trueproperty=article:published_timecontent=2024-12-16T21:30:00.000Z><linkdata-rh=truerel=iconhref=/img/favicon.ico><linkdata-rh=truerel=canonicalhref=https://speice.io/2024/11/playing-with-fire><linkdata-rh=truerel=alternatehref=https://speice.io/2024/11/playing-with-firehreflang=en><linkdata-rh=truerel=alternatehref=https://speice.io/2024/11/playing-with-firehreflang=x-default><scriptdata-rh=truetype=application/ld+json>{"@context":"https://schema.org","@id":"https://speice.io/2024/11/playing-with-fire","@type":"BlogPosting","author":{"@type":"Person","name":"Bradlee Speice"},"dateModified":"2024-12-17T02:30:05.000Z","datePublished":"2024-12-16T21:30:00.000Z","description":"Wikipedia describes fractal flames as:","headline":"Playing with fire: The fractal flame algorithm","isPartOf":{"@id":"https://speice.io/","@type":"Blog","name":"Blog"},"keywords":[],"mainEntityOfPage":"https://speice.io/2024/11/playing-with-fire","name":"Playing with fire: The fractal flame algorithm","url":"https://speice.io/2024/11/playing-with-fire"}</script><linkrel=alternatetype=application/rss+xmlhref=/rss.xmltitle="The Old Speice Guy RSS Feed"><linkrel=alternatetype=application/atom+xmlhref=/atom.xmltitle="The Old Speice Guy Atom Feed"><linkrel=stylesheethref=/katex/katex.min.css><linkrel=stylesheethref=/assets/css/styles.16c3428d.css><scriptsrc=/assets/js/runtime~main.9e43ec1c.jsdefer></script><scriptsrc=/assets/js/main.a9d33393.jsdefer></script><bodyclass=navigation-with-keyboard><script>!function(){vart,e=function(){try{returnnewURLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{returnwindow.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]ofnewURLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){vara=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><divid=__docusaurus><divrole=regionaria-label="Skip to main content"><aclass=skipToContent_fXgnhref=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><navaria-label=Mainclass="navbar navbar--fixed-top"><divclass=navbar__inner><divclass=navbar__items><buttonaria-label="Toggle navigation bar"aria-expanded=falseclass="navbar__toggle clean-btn"type=button><svgwidth=30height=30viewBox="0 0 30 30"aria-hidden=true><pathstroke=currentColorstroke-linecap=roundstroke-miterlimit=10stroke-width=2d="M4 7h22M4 15h22M4 23h22"/></svg></button><aclass=navbar__brandhref=/><divclass=navbar__logo><imgsrc=/img/logo.svgalt="Sierpinski Gasket"class="themedComponent_mlkZ themedComponent--light_NVdE"><imgsrc=/img/logo-dark.svgalt="Sierpinski Gasket"class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><bclass="navbar__title text--truncate">The Old Speice Guy<
<p>I don't remember when exactly I first learned about fractal flames, but I do remember being entranced by the images they created.
I also remember their unique appeal to my young engineering mind; this was an art form I could participate in.</p>
<p>The <ahref=https://flam3.com/flame_draves.pdftarget=_blankrel="noopener noreferrer">Fractal Flame Algorithm paper</a> describing their structure was too much
for me to handle at the time (I was ~12 years old), so I was content to play around and enjoy the pictures.
But the desire to understand it stuck around. Now, with a graduate degree under my belt, I wanted to revisit it.</p>
<p>This guide is my attempt to explain how fractal flames work so that younger me — and others interested in the art —
can understand without too much prior knowledge.</p>
<hr>
<h2class="anchor anchorWithStickyNavbar_LWe7"id=iterated-function-systems>Iterated function systems<ahref=#iterated-function-systemsclass=hash-linkaria-label="Direct link to Iterated function systems"title="Direct link to Iterated function systems"></a></h2>
<p>As mentioned, fractal flames are a type of "<ahref=https://en.wikipedia.org/wiki/Iterated_function_systemtarget=_blankrel="noopener noreferrer">iterated function system</a>,"
or IFS. The formula for an IFS is short, but takes some time to work through:</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id=solution-set>Solution set<ahref=#solution-setclass=hash-linkaria-label="Direct link to Solution set"title="Direct link to Solution set"></a></h3>
<p>First, <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span>. <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span> is the set of points in two dimensions (in math terms, <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi><mo>∈</mo><msup><mimathvariant=double-struck>R</mi><mn>2</mn></msup></mrow><annotationencoding=application/x-tex>S \in \mathbb{R}^2</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.7224em;vertical-align:-0.0391em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span><spanclass=mspacestyle=margin-right:0.2778em></span><spanclass=mrel>∈</span><spanclass=mspacestyle=margin-right:0.2778em></span></span><spanclass=base><spanclass=strutstyle=height:0.8141em></span><spanclass=mord><spanclass="mord mathbb">R</span><spanclass=msupsub><spanclass=vlist-t><spanclass=vlist-r><spanclass=vliststyle=height:0.8141em><spanstyle=top:-3.063em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span>)
that represent a "solution" of some kind to our equation.
Our goal is to find all the points in <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span>, plot them, and display that image.</p>
<p>For example, if we say <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi><mo>=</mo><mostretchy=false>{</mo><mostretchy=false>(</mo><mn>0</mn><moseparator=true>,</mo><mn>0</mn><mostretchy=false>)</mo><moseparator=true>,</mo><mostretchy=false>(</mo><mn>1</mn><moseparator=true>,</mo><mn>1</mn><mostretchy=false>)</mo><moseparator=true>,</mo><mostretchy=false>(</mo><mn>2</mn><moseparator=true>,</mo><mn>2</mn><mostretchy=false>)</mo><mostretchy=false>}</mo></mrow><annotationencoding=application/x-tex>S = \{(0,0), (1, 1), (2, 2)\}</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span><spanclass=mspacestyle=margin-right:0.2778em></span><spanclass=mrel>=</span><spanclass=mspacestyle=margin-right:0.2778em></span></span><spanclass=base><spanclass=strutstyle=height:1em;vertical-align:-0.25em></span><spanclass=mopen>{(</span><spanclass=mord>0</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mord>0</span><spanclass=mclose>)</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mopen>(</span><spanclass=mord>1</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mord>1</span><spanclass=mclose>)</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mopen>(</span><spanclass=mord>2</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mord>2</span><spanclass=mclose>)}</span></span></span></span>, there are three points to plot:</p>
<p>With fractal flames, rather than listing individual points, we use functions to describe the solution.
This means there are an infinite number of points, but if we find <em>enough</em> points to plot, we get a nice picture.
And if the functions change, the solution also changes, and we get something new.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id=transform-functions>Transform functions<ahref=#transform-functionsclass=hash-linkaria-label="Direct link to Transform functions"title="Direct link to Transform functions"></a></h3>
<p>Second, the <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>F</mi><mi>i</mi></msub><mostretchy=false>(</mo><mi>S</mi><mostretchy=false>)</mo></mrow><annotationencoding=application/x-tex>F_i(S)</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:1em;vertical-align:-0.25em></span><spanclass=mord><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:-0.1389em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mathnormal mtight">i</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.15em><span></span></span></span></span></span></span><spanclass=mopen>(</span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span><spanclass=mclose>)</span></span></span></span> functions, also known as "transforms."
Each transform takes in a 2-dimensional point and gives a new point back
While you could theoretically use any function, we'll focus on a specific kind of function
called an "<ahref=https://en.wikipedia.org/wiki/Affine_transformationtarget=_blankrel="noopener noreferrer">affine transformation</a>." Every transform uses the same formula:</p>
<p>Applying this transform to the original points gives us a new set of points:</p>
<!---->
<!---->
<!---->
<divclass=VictoryContainerstyle=height:100%;width:100%;user-select:none;pointer-events:none;touch-action:none;position:relative><svgwidth=450height=300role=imgviewBox="0 0 450 300"style=width:100%;height:100%;pointer-events:all><g><pathd="M 60, 240 m -5, 0 a 5, 5 0 1,0 10,0 a 5, 5 0 1,0 -10,0"style=fill:blue;opacity:1;stroke:transparent;stroke-width:0role=presentationshape-rendering=auto/><pathd="M 192, 188.57142857142858 m -5, 0 a 5, 5 0 1,0 10,0 a 5, 5 0 1,0 -10,0"style=fill:blue;opacity:1;stroke:transparent;stroke-width:0role=presentationshape-rendering=auto/><pathd="M 324, 137.14285714285714 m -5, 0 a 5, 5 0 1,0 10,0 a 5, 5 0 1,0 -10,0"style=fill:blue;opacity:1;stroke:transparent;stroke-width:0role=presentationshape-rendering=auto/></g><g><pathd="M 126, 162.85714285714286 m -5, 0 a 5, 5 0 1,0 10,0 a 5, 5 0 1,0 -10,0"style=fill:orange;opacity:1;stroke:transparent;stroke-width:0role=presentationshape-rendering=auto/><pathd="M 258, 111.42857142857143 m -5, 0 a 5, 5 0 1,0 10,0 a 5, 5 0 1,0 -10,0"style=fill:orange;opacity:1;stroke:transparent;stroke-width:0role=presentationshape-rendering=auto/><pathd="M 390, 60 m -5, 0 a 5, 5 0 1,0 10,0 a 5, 5 0 1,0 -10,0"style=fill:orange;opacity:1;stroke:transparent;stroke-width:0role=presentationshape-rendering=auto/></g><g><rectvector-effect=non-scaling-strokestyle=fill:none;stroke:#E8E8E8;stroke-width:2;padding:16pxrole=presentationshape-rendering=autox=75y=10width=93.44375height=72.98/><pathd="M 97, 32 m -4.8, 0 a 4.8, 4.8 0 1,0 9.6,0 a 4.8, 4.8 0 1,0 -9.6,0"style=fill:blue;type:circlerole=presentationshape-rendering=auto/><pathd="M 97, 58.49 m -4.8, 0 a 4.8, 4.8 0 1,0 9.6,0 a 4.8, 4.8 0 1,0 -9.6,0"style=fill:orange;type:circlerole=presentationshape-rendering=auto/><textid=chart-legend-2-labels-0direction=inheritdx=0x=111.4y=36.26><tspanx=111.4dx=0dy=0text-anchor=startstyle="font-family:'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif;font-size:12px;font-weight:300;letter-spacing:normal;padding:8px;fill:#292929;stroke:transparent">(x,y)</tspan></text><textid=chart-legend-2-labels-1direction=inheritdx=0x=111.4y=62.75><tspanx=111.4dx=0dy=0text-anchor=startstyle="font-family:'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif;font-size:12px;font-weight:300;letter-spacing:normal;padding:8px;fill:#292929;stroke:transparent">F(x,y)</tspan></text></g><grole=presentation><linevector-effect=non-scaling-strokestyle=stroke:#757575;fill:transparent;stroke-width:1;stroke-linecap:round;stroke-linejoin:roundrole=presentationshape-rendering=autox1=60x2=390y1=240y2=240/><grole=presentation><textid=chart-axis-3-tickLabels-0direction=inheritdx=0x=126y=263.26><tspanx=126dx=0dy=0text-anchor=middlestyle="font-family:'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif;font-size:12px;font-weight:300;letter-spacing:normal;padding:8px;fill:#292929;stroke:transparent">0.5</tspan></text></g><grole=presentation><textid=chart-axis-3-tickLabels-1direction=inheritdx=0x=192y=263.26><tspanx=192dx=0dy=0text-anchor=middlestyle="font-family:'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif;font-size:12px;font-weight:300;letter-spacing:normal;padding:8px;fill:#292929;stroke:transparent">1.0</tspan></text></g><grole=presentation><textid=chart-axis-3-tickLabels-2direction=inheritdx=0x=258y=263.26><tspanx=258dx=0dy=0text-anchor=middlestyle="font-family:'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif;font-size:12px;font-weight:300;letter-spacing:normal;padding:8px;fill:#292929;stroke:transparent">1.5</tspan></text></g><grole=presentation><textid=chart-axis-3-tickLabels-3direction=inheritdx=0x=324y=263.26><tspanx=324dx=0dy=0text-anchor=middlestyle="font-family:'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif;font-size:12px;font-weight:300;letter-spacing:normal;padding:8px;fill:#292929;stroke:transparent">2.0</tspan></text></g><grole=presentation><textid=chart-axis-3-tickLabels-4direction=inheritdx=0x=390y=263.26><tspan
<p>Fractal flames use more complex functions, but they all start with this structure.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id=fixed-set>Fixed set<ahref=#fixed-setclass=hash-linkaria-label="Direct link to Fixed set"title="Direct link to Fixed set"></a></h3>
<p>With those definitions in place, let's revisit the initial problem:</p>
<p>Our solution, <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span>, is the union of all sets produced by applying each function, <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>F</mi><mi>i</mi></msub></mrow><annotationencoding=application/x-tex>F_i</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.8333em;vertical-align:-0.15em></span><spanclass=mord><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:-0.1389em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mathnormal mtight">i</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.15em><span></span></span></span></span></span></span></span></span></span>,
to points in the solution.</p>
</blockquote>
<p>There's just one small problem: to find the solution, we must already know which points are in the solution.
What?</p>
<p>John E. Hutchinson provides an explanation in the <ahref=https://maths-people.anu.edu.au/~john/Assets/Research%20Papers/fractals_self-similarity.pdftarget=_blankrel="noopener noreferrer">original paper</a>
defining the mathematics of iterated function systems:</p>
<blockquote>
<p>Furthermore, <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span> is compact and is the closure of the set of fixed points <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>s</mi><mrow><msub><mi>i</mi><mn>1</mn></msub><mimathvariant=normal>.</mi><mimathvariant=normal>.</mi><mimathvariant=normal>.</mi><msub><mi>i</mi><mi>p</mi></msub></mrow></msub></mrow><annotationencoding=application/x-tex>s_{i_1...i_p}</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.7779em;vertical-align:-0.3473em></span><spanclass=mord><spanclass="mord mathnormal">s</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:0em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mtight"><spanclass="mord mtight"><spanclass="mord mathnormal mtight">i</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3173em><spanstyle=top:-2.357em;margin-left:0em;margin-right:0.0714em><spanclass=pstrutstyle=height:2.5em></span><spanclass="sizing reset-size3 size1 mtight"><spanclass="mord mtight">1</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.143em><span></span></span></span></span></span></span><spanclass="mord mtight">...</span><spanclass="mord mtight"><spanclass="mord mathnormal mtight">i</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.1645em><spanstyle=top:-2.357em;margin-left:0em;margin-right:0.0714em><spanclass=pstrutstyle=height:2.5em></span><spanclass="sizing reset-size3 size1 mtight"><spanclass="mord mathnormal mtight">p</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.2819em><span></span></span></span></span></span></span></span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.3473em><span></span></span></span></span></span></span></span></span></span>
of finite compositions <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>F</mi><mrow><msub><mi>i</mi><mn>1</mn></msub><mimathvariant=normal>.</mi><mimathvariant=normal>.</mi><mimathvariant=normal>.</mi><msub><mi>i</mi><mi>p</mi></msub></mrow></msub></mrow><annotationencoding=application/x-tex>F_{i_1...i_p}</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:1.0307em;vertical-align:-0.3473em></span><spanclass=mord><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:-0.1389em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mtight"><spanclass="mord mtight"><spanclass="mord mathnormal mtight">i</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3173em><spanstyle=top:-2.357em;margin-left:0em;margin-right:0.0714em><spanclass=pstrutstyle=height:2.5em></span><spanclass="sizing reset-size3 size1 mtight"><spanclass="mord mtight">1</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.143em><span></span></span></span></span></span></span><spanclass="mord mtight">...</span><spanclass="mord mtight"><spanclass="mord mathnormal mtight">i</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.1645em><spanstyle=top:-2.357em;margin-left:0em;margin-right:0.0714em><spanclass=pstrutstyle=height:2.5em></span><spanclass="sizing reset-size3 size1 mtight"><spanclass="mord mathnormal mtight">p</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.2819em><span></span></span></span></span></span></span></span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.3473em><span></span></span></span></span></span></span></span></span></span> of members of <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>F</mi></mrow><annotationencoding=application/x-tex>F</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span></span></span></span>.</p>
</blockquote>
<p>Before your eyes glaze over, let's unpack this:</p>
<ul>
<li><strong>Furthermore, <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span> is <ahref=https://en.wikipedia.org/wiki/Compact_spacetarget=_blankrel="noopener noreferrer">compact</a>...</strong>: All points in our solution will be in a finite range</li>
<li><strong>...and is the <ahref=https://en.wikipedia.org/wiki/Closure_(mathematics)target=_blankrel="noopener noreferrer">closure</a> of the set of <ahref=https://en.wikipedia.org/wiki/Fixed_point_(mathematics)target=_blankrel="noopener noreferrer">fixed points</a></strong>:
Applying our functions to points in the solution will give us other points that are in the solution</li>
<li><strong>...of finite compositions <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>F</mi><mrow><msub><mi>i</mi><mn>1</mn></msub><mimathvariant=normal>.</mi><mimathvariant=normal>.</mi><mimathvariant=normal>.</mi><msub><mi>i</mi><mi>p</mi></msub></mrow></msub></mrow><annotationencoding=application/x-tex>F_{i_1...i_p}</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:1.0307em;vertical-align:-0.3473em></span><spanclass=mord><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:-0.1389em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mtight"><spanclass="mord mtight"><spanclass="mord mathnormal mtight">i</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3173em><spanstyle=top:-2.357em;margin-left:0em;margin-right:0.0714em><spanclass=pstrutstyle=height:2.5em></span><spanclass="sizing reset-size3 size1 mtight"><spanclass="mord mtight">1</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.143em><span></span></span></span></span></span></span><spanclass="mord mtight">...</span><spanclass="mord mtight"><spanclass="mord mathnormal mtight">i</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.1645em><spanstyle=top:-2.357em;margin-left:0em;margin-right:0.0714em><spanclass=pstrutstyle=height:2.5em></span><spanclass="sizing reset-size3 size1 mtight"><spanclass="mord mathnormal mtight">p</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.2819em><span></span></span></span></span></span></span></span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.3473em><span></span></span></span></span></span></span></span></span></span> of members of <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>F</mi></mrow><annotationencoding=application/x-tex>F</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span></span></span></span></strong>: By composing our functions (that is,
using the output of one function as input to the next), we will arrive at the points in the solution</li>
</ul>
<p>Thus, by applying the functions to fixed points of our system, we will find the other points we care about.</p>
<detailsclass="details_lb9f alert alert--info details_b_Ee"data-collapsed=true><summary>If you want a bit more math...</summary><div><divclass=collapsibleContent_i85q><p>...then there are some extra details I've glossed over so far.<p>First, the Hutchinson paper requires that the functions <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>F</mi><mi>i</mi></msub></mrow><annotationencoding=application/x-tex>F_i</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.8333em;vertical-align:-0.15em></span><spanclass=mord><spanclass="mord mathnormal"style=margin-right:0.13889em>F</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:-0.1389em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mathnormal mtight">i</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.15em><span></span></span></span></span></span></span></span></span></span> be <em>contractive</em> for the solution set to exist.
That is, applying the function to a point must bring it closer to other points. However, as the fractal flame
algorithm demonstrates, we only need functions to be contractive <em>on average</em>. At worst, the system will
degenerate and produce a bad image.<p>Second, we're focused on <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msup><mimathvariant=double-struck>R</mi><mn>2</mn></msup></mrow><annotationencoding=application/x-tex>\mathbb{R}^2</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.8141em></span><spanclass=mord><spanclass="mord mathbb">R</span><spanclass=msupsub><spanclass=vlist-t><spanclass=vlist-r><spanclass=vliststyle=height:0.8141em><spanstyle=top:-3.063em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> because we're generating images, but the math
allows for arbitrary dimensions; you could also have 3-dimensional fractal flames.<p>Finally, there's a close relationship between fractal flames and <ahref=https://en.wikipedia.org/wiki/Attractortarget=_blankrel="noopener noreferrer">attractors</a>.
Specifically, the fixed points of <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>S</mi></mrow><annotationencoding=application/x-tex>S</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6833em></span><spanclass="mord mathnormal"style=margin-right:0.05764em>S</span></span></span></span> act as attractors for the chaos game (explained below).</div></div></details>
<p>This is still a bit vague, so let's work through an example.</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id=sierpinskis-gasket><ahref=https://www.britannica.com/biography/Waclaw-Sierpinskitarget=_blankrel="noopener noreferrer">Sierpinski's gasket</a><ahref=#sierpinskis-gasketclass=hash-linkaria-label="Direct link to sierpinskis-gasket"title="Direct link to sierpinskis-gasket"></a></h2>
<p>The Fractal Flame paper gives three functions to use for a first IFS:</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id=the-chaos-game>The chaos game<ahref=#the-chaos-gameclass=hash-linkaria-label="Direct link to The chaos game"title="Direct link to The chaos game"></a></h3>
<p>Now, how do we find the "fixed points" mentioned earlier? The paper lays out an algorithm called the "<ahref=https://en.wikipedia.org/wiki/Chaos_gametarget=_blankrel="noopener noreferrer">chaos game</a>"
<p>Let's turn this into code, one piece at a time.</p>
<p>To start, we need to generate some random numbers. The "bi-unit square" is the range <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mostretchy=false>[</mo><mo>−</mo><mn>1</mn><moseparator=true>,</mo><mn>1</mn><mostretchy=false>]</mo></mrow><annotationencoding=application/x-tex>[-1, 1]</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:1em;vertical-align:-0.25em></span><spanclass=mopen>[</span><spanclass=mord>−</span><spanclass=mord>1</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mord>1</span><spanclass=mclose>]</span></span></span></span>,
<p>Next, we need to choose a random integer from <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mn>0</mn></mrow><annotationencoding=application/x-tex>0</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6444em></span><spanclass=mord>0</span></span></span></span> to <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow><annotationencoding=application/x-tex>n - 1</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.6667em;vertical-align:-0.0833em></span><spanclass="mord mathnormal">n</span><spanclass=mspacestyle=margin-right:0.2222em></span><spanclass=mbin>−</span><spanclass=mspacestyle=margin-right:0.2222em></span></span><spanclass=base><spanclass=strutstyle=height:0.6444em></span><spanclass=mord>1</span></span></span></span>:</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id=plotting>Plotting<ahref=#plottingclass=hash-linkaria-label="Direct link to Plotting"title="Direct link to Plotting"></a></h3>
<p>Finally, implementing the <code>plot</code> function. This blog series is interactive,
so everything displays directly in the browser. As an alternative,
software like <code>flam3</code> and Apophysis can "plot" by saving an image to disk.</p>
<p>To see the results, we'll use the <ahref=https://developer.mozilla.org/en-US/docs/Web/API/Canvas_APItarget=_blankrel="noopener noreferrer">Canvas API</a>.
This allows us to manipulate individual pixels in an image and show it on screen.</p>
<p>First, we need to convert from fractal flame coordinates to pixel coordinates.
To simplify things, we'll assume that we're plotting a square image
with range <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mostretchy=false>[</mo><mn>0</mn><moseparator=true>,</mo><mn>1</mn><mostretchy=false>]</mo></mrow><annotationencoding=application/x-tex>[0, 1]</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:1em;vertical-align:-0.25em></span><spanclass=mopen>[</span><spanclass=mord>0</span><spanclass=mpunct>,</span><spanclass=mspacestyle=margin-right:0.1667em></span><spanclass=mord>1</span><spanclass=mclose>]</span></span></span></span> for both <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>x</mi></mrow><annotationencoding=application/x-tex>x</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.4306em></span><spanclass="mord mathnormal">x</span></span></span></span> and <spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><mi>y</mi></mrow><annotationencoding=application/x-tex>y</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.625em;vertical-align:-0.1944em></span><spanclass="mord mathnormal"style=margin-right:0.03588em>y</span></span></span></span>:</p>
<p>Next, we'll store the pixel data in an <ahref=https://developer.mozilla.org/en-US/docs/Web/API/ImageDatatarget=_blankrel="noopener noreferrer"><code>ImageData</code> object</a>.
Each pixel on screen has a corresponding index in the <code>data</code> array.
To plot a point, we set that pixel to be black:</p>
<small><p>The image here is slightly different than in the paper.
I think the paper has an error, so I'm plotting the image
like the <ahref=https://github.com/scottdraves/flam3/blob/7fb50c82e90e051f00efcc3123d0e06de26594b2/rect.c#L440-L441target=_blankrel="noopener noreferrer">reference implementation</a>.</small>
<h3class="anchor anchorWithStickyNavbar_LWe7"id=weights>Weights<ahref=#weightsclass=hash-linkaria-label="Direct link to Weights"title="Direct link to Weights"></a></h3>
<p>There's one last step before we finish the introduction. So far, each transform has
the same chance of being picked in the chaos game.
We can change that by giving them a "weight" (<spanclass=katex><spanclass=katex-mathml><math><semantics><mrow><msub><mi>w</mi><mi>i</mi></msub></mrow><annotationencoding=application/x-tex>w_i</annotation></semantics></math></span><spanclass=katex-htmlaria-hidden=true><spanclass=base><spanclass=strutstyle=height:0.5806em;vertical-align:-0.15em></span><spanclass=mord><spanclass="mord mathnormal"style=margin-right:0.02691em>w</span><spanclass=msupsub><spanclass="vlist-t vlist-t2"><spanclass=vlist-r><spanclass=vliststyle=height:0.3117em><spanstyle=top:-2.55em;margin-left:-0.0269em;margin-right:0.05em><spanclass=pstrutstyle=height:2.7em></span><spanclass="sizing reset-size6 size3 mtight"><spanclass="mord mathnormal mtight">i</span></span></span></span><spanclass=vlist-s></span></span><spanclass=vlist-r><spanclass=vliststyle=height:0.15em><span></span></span></span></span></span></span></span></span></span>) instead:</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id=summary>Summary<ahref=#summaryclass=hash-linkaria-label="Direct link to Summary"title="Direct link to Summary"></a></h2>
<p>Studying the foundations of fractal flames is challenging,
but we now have an understanding of the mathematics
and the implementation of iterated function systems.</p>