Skip to content

Instantly share code, notes, and snippets.

@fgnass
Forked from 140bytes/LICENSE.txt
Created October 4, 2011 08:44
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fgnass/1261166 to your computer and use it in GitHub Desktop.
Save fgnass/1261166 to your computer and use it in GitHub Desktop.
The Mandelbro™

The Mandelbro™

The code was golfed down to 140 bytes during JSConf EU 2011, inspired by a slide from Jed Schmidt's talk:

yo

Credits go to @sylvinus who came up with the idea and the original code as well as @p01 and Jacob Seidelin who pioneered tweet-sized ASCII art fractals back in 2008.

Instead of drawing onto a canvas, the gist uses █ characters (U+2588) with a font-size of 1px. In order to make this work, the target DIV must have the following styles:

font-size:1px;
line-height:1px;
width:300px;
word-wrap:break-word;

To support Firefox on OSX, we also have to set a font in which the FULL BLOCK character is exactly 1em wide. Helvetica works well here. All other browsers, including Firefox on Windows and even IE6 don't care about the font. But be warned, it takes a while in old IEs to render the 90000 font tags.

Besides the use of String.prototype.fontcolor() this gist features some other nifty tricks, like using bitmasks and exploiting the parsing rules for legacy color values which are all documented in the annotated version.

If you want to learn more about the Mandelbrot set itself, check out the this comprehensive tutorial.

And here is a jsFiddle to play with.

function(
s, // Array to hold the result
r,i,k,t,n // Placeholder vars
){
for(
n=9e4; // Loop over all 300 x 300 pixel
n--; // until n is 0 is reached
s[n]= // and set the current pixel
'█' // to the FULL BOCK 3-byte unicode character (U+2588)
.fontcolor( // and wrap it in a <font color=""> tag.
// Bonus: As the font tag uses legacy color values, we don't need a leading #!
// Since we iterate exactly 64 times, which is 1000000 in binary,
(k&63)*4 // the fill color will be black (with a slightly red tint) on the
// inside of the set (111111 & 1000000 == 0).
+1e5) // Finally we add 100000 in order to shift the value to the right (blue),
// so the lightest color will be #1000f8 while the darkest is #100000.
)
for(r=i=k=0; // Calculate the Mandelbrot set, see http://warp.povusers.org/Mandelbrot
r*r+i*i+k++<63; // for a good explanation.
i=t)
t=2*r*i+1
-n/45e3, // Center vertically, 300*300/2
r=r*r-i*i+1
-n/100%3 // Center horizontally, 300/3
}
function(s,r,i,k,t,n){for(n=9e4;n--;s[n]='█'.fontcolor((k&63)*4+1e5))for(r=i=k=0;r*r+i*i+k++<63;i=t)t=2*r*i+1-n/45e3,r=r*r-i*i+1-n/100%3}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 Felix Gnass <http://twitter.com/fgnass>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "Mandelbrot Set",
"description": "Renders the Mandelbrot fractal set",
"keywords": [
"fractal",
"mandelbrot",
"fontcolor"
]
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Mandelbro™</title>
<style>
#out {
font-size: 2px;
line-height: 1px;
width: 300px;
word-wrap:break-word; /* Force line break after 300px */
font-family: "Arial Black", Helvetica, Verdana;
}
font {
display: inline-block;
width: 1px;
height: 1px;
}
</style>
</head>
<body>
<div id="out"></div>
<script>
var mandelbrot =
function(s,r,i,k,t,n){for(n=9e4;n--;s[n]='█'.fontcolor((k&63)*4+1e5))for(r=i=k=0;r*r+i*i+k++<63;i=t)t=2*r*i+1-n/45e3,r=r*r-i*i+1-n/100%3}
var a = [];
mandelbrot(a);
document.getElementById('out').innerHTML = a.join('');
</script>
</body>
</html>
@tsaniel
Copy link

tsaniel commented Oct 5, 2011

It's really amazing :)
Sadly, my browser could not render it correctly :(

@mathiasbynens
Copy link

Heads up: the README currently says 136 bytes, but it’s actually 135 characters or 137 bytes, according to the UTF-8 byte counter.

@fgnass
Copy link
Author

fgnass commented Oct 5, 2011

@tsaniel Which browser did you use on which OS? Also, did you view the jsFiddle or did you try it locally? Rendering is broken if the page isn't interpreted as UTF-8.

@fgnass
Copy link
Author

fgnass commented Oct 5, 2011

@mathiasbynens Thanks, I fixed the README. Good that we left enough room in the code ;)

@tsaniel
Copy link

tsaniel commented Oct 5, 2011

@fgnass I am using 12.0.738.0 on Windows XP. I have tried it locally and on jsFiddle, both turn out like this:

https://i.minus.com/iuRs77TSDEqdt.png

I guess this is not related to the screen resolution?

@p01
Copy link

p01 commented Oct 5, 2011

FWIW, back then I got an ASCII art Mandelbrot set in 111 bytes of DHTML after some golfing with Jacob Seidelin

Same problem as @tsaniel here in various versions of Opera 12 on Windows 7 and XP pro.

How about saving 5 bytes ?

function(s,r,i,k,n){for(n=9e4;n--;s[n]='█'.fontcolor(k*4+1e5))for(r=i=k=0;r*r+i*i+k++<62;i=t)t=2*r*i+1-n/45e3,r=r*r-i*i+1-n/100%3}

@fgnass
Copy link
Author

fgnass commented Oct 5, 2011

@p01 Viewing your 111b version in Chrome or Firefox only displays a blank page, is why I linked to the 137b version in the README instead.

I updated the CSS to use 1x1 pixel inline-blocks which seems to work better in terms of cross-browser support.

@p01
Copy link

p01 commented Oct 5, 2011

It used to work in FF. Some browsers require a document.close() :
btw you forgot the http:// in that link ;)

@fgnass
Copy link
Author

fgnass commented Oct 5, 2011

@p01 About saving the five bytes: I actually added this feature to pack as much functionality as possible into the available space. But I'd happily sacrifice it for a cooler feature if we could fill up the whole 140 bytes!

BTW, adding a > sign to the end of your 111b version would restore FF support. And by replacing the literal 0x1F linebreak with \n it would work in even more browsers. Two bytes extra for cross-browser support doesn't sound like a bad deal.

@p01
Copy link

p01 commented Oct 5, 2011

The 5 bytes saving is not removing any functionality. The (k&63) is useless because we know for sure that k is an Integer in the range [ 0 ; 63 ] per the for(r=i=k=0;r*r+i*i+k++<63;i=t). The variable kstarts at 0, increases by 1 at each iteration, and even if r*r+i*i is 0, k can not exceed 63. ;)

The missing > was Jacob's finding. Alas it set us back on cross-browsers compatibility.
Make sure to check the other versions Jacob and I wrote and our discussion/comments.

@p01
Copy link

p01 commented Oct 8, 2011

Your variable tis leaking into the global namespace.

Good news, as noted by @tsaniel on the other Mandelbrot you can save 2 bytes by moving all the variables of the inner loop in the body of the function:

function(s,n){for(n=9e4;n--;s[n]='¦'.fontcolor(k*4+1e5))for(var r=i=k=t=0;r*r+i*i+k++<62;i=t)t=2*r*i+1-n/45e3,r=r*r-i*i+1-n/100%3}

130bytes

@tsaniel
Copy link

tsaniel commented Oct 8, 2011

@p01: That was my bad. Actually, in this case the variables still leak to global scope.

@fgnass
Copy link
Author

fgnass commented Oct 9, 2011

@p01 Thanks, good catch. I fixed the leakage and the code is now at exactly 140 bytes.

The idea of (k&63) is to paint all values black that belong to the set. Without these extra bytes the actual set ends up being colored in bright blue and is not very well distinguishable from the rest.

@tsaniel
Copy link

tsaniel commented Aug 5, 2012

(k&63)*4 -> k%64*4?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment