Drawing the Mandelbrot set in Ruby and Haskell
Generating the wonderful Mandelbrot set in Ruby and Haskell – it’s amazing how simple math can give birth to such a sophisticated shape. It’s elegance fits Ruby and Haskell well. Come closer and see the wonders!
I just came across one of my favourie books which I read when I was a kid: ‘The emperor’s new mind‘ by Roger Penrose. I remember I was very enthusiastic reading it and became amazed by fractals – especially the good old Mandelbrot set. I wasn’t really into programming back then – I was like 15, only starting out with *blush* Pascal, but now I’m equipped with two wonderful languages, so I decided to code my childhood dream.
Some very basic maths for the things to come:
The Mandelbrot set is a set of complex numbers for which the simple iterative polynomial z(0) = 0; z(n+1) = z(n)^2 + c remains bounded.
It’s the boundary of the set which is a fractal, an ever-zoomable, self-repeating source of wonders. The complex plane is a two dimensional plane on which the x-axis is the real part and the y-axis is the imaginary part of any complex number (at least let’s make this our convention), thus every complex number defines a point on this plane.
After some trial and error, the following area of the complex plane is best used for the well-known generic Mandelbrot fractal:
x: (-2.0) – 0.5
y: (-1.0) – 1.0
The algorithm is this: for every point (complex number) inside this area we check if the iterative polynomial remains bounded. Of course we can’t check up to infinity, so we need to define some maximum iteration limit, say 500 (for our console plotting, a much lower value would be good enough). If the magnitude of the 500th iteration of the polynomial is lower than 2.0 (a hand-picked but close-enough upper limit), then the given point is part of the Mandelbrot set.
Let’s code a quick console plotter in ruby:
require 'complex' def mandelbrot(a) Array.new(500,a).inject(a) { |z,c| z*z + c } end (1.0).step(-1,-0.05) do |y| (-2.0).step(0.5,0.0315) do |x| print mandelbrot(Complex(x,y)).abs < 2 ? '*' : ' ' end puts end |
Pretty straightforward, I think. All numbers are hard-coded for simplicity (iteration step size is calculated based on the x-y range and the number of characters in our terminal). One neat trick is the iteration itself: Enumerable#inject is used instead of classic looping. I just love ruby’s functional side. Array.new(500,a) generates an array by repeating ‘a’ 500 times. Inject is fold in a disguise, reducing the array to one element by the operations given in the block.
It produces the following cool little output:

Pretty nice for starters. Let’s see the Haskell version.
import Data.Complex mandelbrot a = iterate (\z -> z^2 + a) a !! 500 main = mapM_ putStrLn [[if magnitude (mandelbrot (x :+ y)) < 2 then '*' else ' ' | x <- [-2, -1.9685 .. 0.5]] | y <- [1, 0.95 .. -1]] |
Some definitions here:
- ‘iterate’ by definition: iterate f x returns an infinite list of repeated applications of f to x: iterate f x == [x, f x, f (f x), ...]
Quite clear, I think – just what we need. - list !! n: take the nth item from the list
- mapM_ by definition: Evaluate each action in the sequence from left to right, and ignore the results. What it does is actually ‘executing’ the operations on its right without using their results – outputting the mandelbrot set itself.
I have one more thing up in my sleeve: a full-fledged ruby drawing version using RMagick, so here it is without comments, use it to your liking:
require 'rubygems' require 'complex' require 'RMagick' include Magick # this is the interesting area of the complex plane X_START = -2.0 X_END = 0.5 Y_START = -1.0 Y_END = 1.0 # wanted image dimensions WIDTH = 800.0 HEIGHT = 600.0 # set this to higher values and sleep well :) ITERATIONS = 200 STEP_X = (X_END - X_START) / WIDTH STEP_Y = (Y_END - Y_START) / HEIGHT def mandelbrot(a) Array.new(ITERATIONS, a).inject(a) { |z,c| z*z + c } end complex_plane = Image.new(WIDTH.to_i, HEIGHT.to_i) MIN_X = x_pixel = 0 y_pixel = 0 x = X_START y = Y_START while y < Y_END x_pixel = 0 x = X_START while x < X_END mandelbrot(Complex(x,y)).abs < 2 ? Draw.new.fill('#000000').point(x_pixel,y_pixel).draw(complex_plane) : nil x_pixel += 1 x += STEP_X end y_pixel += 1 y += STEP_Y end complex_plane.write("mandelbrot.png") |
I hope you liked this quick and simple post :) I hacked this together in 30 minutes, so I know it’s not high quality code, but I did it for fun mostly.
Related posts:
18 comments
Leave a Reply
Additional comments powered by BackType


Generating the Mandelbrot set in #Ruby and #Haskell http://is.gd/9xE9w
This comment was originally posted on Twitter
[Reply]
>> list !! n: take the first n items from the list
No it doesn’t. It takes the n-item from the list. To take the first n items you should use take.
[1..3] !! 2 -> 3
take 2 [1..3] -> [1,2]
[Reply]
ochronus Reply:
March 3rd, 2010 at 07:23
@Diego, thanks, it was a blooper, of course I meant nth item :) It’s corrected now
[Reply]
Drawing the Mandelbrot set in Ruby and Haskell http://icio.us/slue1d
This comment was originally posted on Twitter
[Reply]
Drawing the Mandelbrot set in Ruby and Haskell http://bit.ly/bCVUy2 #haskell #mandelbrot #math #ruby #cs
This comment was originally posted on Twitter
[Reply]
Beautiful :) I wish I understood the "inject" syntax… but unfortunately, I do not. I’m pretty new to Ruby.
This comment was originally posted on Reddit
[Reply]
It’s like ‘collapsing’ the array, squashing it into one value. A simpler example: irb(main):006:0> [1,2,3,4].inject {|a,b| puts "#{a};#{b}" ; a+b} 1;2 3;3 6;4 => 10 I hope it’s clearer now
This comment was originally posted on Reddit
[Reply]
also for a better reference, read this: http://en.wikipedia.org/wiki/Fold_(higher-order_function)
This comment was originally posted on Reddit
[Reply]
Not really :( I’m still rather confused. Maybe you could just use 1 variable instead of a,b ?
This comment was originally posted on Reddit
[Reply]
You have a mistake on the inner while loop. You increment y_pixel when you should increment x_pixel
[Reply]
ochronus Reply:
March 4th, 2010 at 06:20
@Patrick, Thanks, nice catch! Corrected.
[Reply]
No, the point is that after step #1, a holds the current aggregated value
This comment was originally posted on Reddit
[Reply]
Drawing the Mandelbrot set in Ruby and Haskell – http://su.pr/26BuuN
This comment was originally posted on Twitter
[Reply]
Drawing the Mandelbrot set in Ruby and Haskell http://bit.ly/d0oWF4 via @AddToAny
This comment was originally posted on Twitter
[Reply]
Excellent site you have here, beautiful stuff. BTW – Thanks for the comment on my site. :)
[Reply]
ochronus Reply:
June 22nd, 2010 at 11:52
@Joey Primiani, thanks for the kind words :)
[Reply]
Some small changes that make it colored:
COLORS = 0xfffff / ITERATIONS
def escapes?(c)
z = 0
ITERATIONS.times do |x|
return x if z.abs > 2
z = z*z + c
end
false
end
And change the central part to:
escape = escapes?(Complex(x,y))
Draw.new.fill(“#%06x” % (escape * COLORS)).point(x_pixel,y_pixel).draw(complex_plane) if escape
[Reply]
ochronus Reply:
July 7th, 2010 at 20:45
@Roger Braun, thanks, it’s a very nice tip!
[Reply]