# An initial rendering of the Mandelbrot set using canvas

Building on information presented in A brief overview of the Mandelbrot set, we describe here a basic method for rendering the Mandelbrot set using canvas.

Before presenting an algorithm for drawing the Mandelbrot set, we first create a JavaScript "class" for manipulating complex numbers. As indicated in A brief overview of the Mandelbrot set, the only operations needed are the ability to square, add, and take the absolute value of complex numbers.

```
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Useful Complex Operations</title>
<style>
body {
text-align: center;
}
</style>
</head>
<body>
<h1>Useful Complex Operations</h1>
<script>
function Complex(x, y) {
// Constructs the complex number x + yi.
this.x = x || 0; // Default to 0 if this parameter is undefined.
this.y = y || 0;
} // Complex
Complex.prototype.toString = function() {
// Returns a string representing this complex number in the form "x + yi".
return this.y >= 0 ? this.x + " + " + this.y + "i" : this.x + " - " + (-this.y) + "i";
} // toString
Complex.prototype.modulus = function() {
// Returns a real number equal to the absolute value of this complex number.
return Math.sqrt(this.x*this.x + this.y*this.y);
} // modulus
Complex.prototype.add = function(z) {
// Returns a complex number equal to the sum of the given complex number and this complex number.
return new Complex(this.x + z.x, this.y + z.y);
} // sum
Complex.prototype.square = function() {
// Returns a complex number equal to the square of this complex number.
var x = this.x*this.x - this.y*this.y;
var y = 2*this.x*this.y;
return new Complex(x, y);
} // square
var z1 = new Complex(0.2, 0.4);
var z2 = new Complex(-0.6, 0.5);
var z3 = z1.add(z2.square()); // Square z2 and add z1 to it.
alert(z3.toString()); // Print the complex result in x + yi form.
alert(z3.modulus()); // Print absolute value of z3.
</script>
</body>
</html>
```

In the Useful complex operations example, we have:

*z*₁ = 0.2 + 0.4*i**z*₂ = -0.6 + 0.5*i**z*₃ = (*z*₂)² +*z*₁ = (-0.6 + 0.5*i*)² + (0.2 + 0.4*i*) = 0.31 - 0.2*i*- |
*z*₃| = z3.modulus() ≈ 0.3689

With the complex JavaScript "class" in place, we present a simple algorithm for drawing the Mandelbrot set:

Divide a 4 x 4 square, centered at the complex origin, into a large number of grid points (not shown):

Treat each grid point as a

*c*value.For each such

*c*value, check whether |*z*ₙ| escapes within a reasonable number of iterations (under*z*ₙ₊₁ =*z*ₙ +*c*; where*z*₀ = 0).If not, color

*c*black (black indicates that*c*is a member of the Mandelbrot set). This assumes we're drawing the Mandelbrot set on a white background.

Implementing this algorithm in JavaScript produces an image similar to this:

In this image, both red coordinate axes have a length of 4. That is, a circle of radius 2, centered at the origin of the complex plane, would exactly contain these axes. As stated previously, the Mandelbrot set lies entirely within a circle of radius 2, and this fact is leveraged in the code used to create this image, as shown next:

```
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Mandelbrot 1</title>
<style>
body {
text-align: center;
}
canvas {
border: 1px black solid;
}
</style>
</head>
<body>
<h1>Mandelbrot 1</h1>
<p>This example demonstrates a basic algorithm for drawing the Mandelbrot set using complex plane coordinates.</p>
<canvas width="600" height="600">Canvas not supported - upgrade your browser</canvas>
<script>
var CPS = 2; // CPS stands for "complex plane square". That is, we are examining a 2*CPS by 2*CPS square region of the complex plane such that this square (or box) is centered at the complex plane's origin.
var MAX_ITERATIONS = 300; // Increase to improve detection of complex c values that belong to the Mandelbrot set.
var DELTA = 0.008; // Decreasing this value increases the number of "pixels" on the canvas, thereby increasing the size of the rendering but without losing image resolution.
function Complex(x, y) {
// Constructs the complex number x + yi. If any parameter is undefined, 0 is used instead.
this.x = x || 0;
this.y = y || 0;
} // Complex
Complex.prototype.toString = function() {
// Returns a string representing this complex number in the form "x + yi".
return this.y >= 0 ? this.x + " + " + this.y + "i" : this.x + " - " + (-this.y) + "i";
} // toString
Complex.prototype.modulus = function() {
// Returns a real number equal to the absolute value of this complex number.
return Math.sqrt(this.x*this.x + this.y*this.y);
} // modulus
Complex.prototype.add = function(z) {
// Returns a complex number equal to the sum of the given complex number and this complex number.
return new Complex(this.x + z.x, this.y + z.y);
} // sum
Complex.prototype.square = function() {
// Returns a complex number equal to the square of this complex number.
var x = this.x*this.x - this.y*this.y;
var y = 2*this.x*this.y;
return new Complex(x, y);
} // square
var globals = {}; // Store all would-be-global-variables in one handy global object.
globals.canvas = document.getElementsByTagName('canvas')[0];
globals.canvas.ctx = globals.canvas.getContext('2d');
globals.canvas.ctx.fillStyle = "black";
initializeCoordinateSystem();
drawMandelbrotSet();
drawCoordinateAxes();
function initializeCoordinateSystem() {
var ctx = globals.canvas.ctx;
ctx.translate(globals.canvas.width / 2, globals.canvas.height / 2); // Move the canvas's coordinate system to the center of the canvas.
ctx.scale(1/DELTA, -1/DELTA); // Flip the y-axis to produce a standard Cartesian coordinate system and scale the canvas coordinate system to match the region of the complex plane under consideration.
} // initializeCoordinateSystem
function drawMandelbrotSet() {
var ctx = globals.canvas.ctx;
for (var Re = -CPS; Re <= CPS; Re = Re + DELTA) { // Represents the Re-axis. Re represents the real part of a complex c value.
next_c_value: // "continue next_c_value;" is equivalent to an old school GOTO statement (which can be very handy in deeply nested loops).
for (var Im = -CPS; Im <= CPS; Im = Im + DELTA) { // Represents the Im-axis. Im represents the imaginary part of a complex c value.
var z = new Complex(0, 0); // Represents Zo (where "o" indicates subscript 0).
var c = new Complex(Re, Im); // Represents a complex c value, which either does or does not belong to the Mandelbrot set, as determined in the next FOR loop.
for (var iterationCount = 1; iterationCount <= MAX_ITERATIONS; iterationCount++) {
z = c.add( z.square() ); // Performs Zn+1 = (Zn)^2 + c
if (z.modulus() > 2) {
continue next_c_value; // The complex c value is not part of the Mandelbrot set, so immediately check the next one.
} // if
} // for
// Assert: z.modulus() <= 2, therefore the complex c value is probably a member of the Mandelbrot set - increase MAX_ITERATIONS to improve this determination.
ctx.fillRect(Re, Im, DELTA, DELTA); // This c value is probably part of the Mandelbrot set, so color this pixel black. A "pixel" for the canvas is a DELTA x DELTA black square.
} // for
} // for
} // drawMandelbrotSet
function drawCoordinateAxes() {
/*
Draws coordinate axes that are exactly as long as the (square) complex plane region under consideration.
*/
var ctx = globals.canvas.ctx;
ctx.lineWidth = DELTA;
ctx.strokeStyle = "red";
// Draw the x-axis:
ctx.beginPath();
ctx.moveTo(CPS, 0);
ctx.lineTo(-CPS, 0);
ctx.stroke();
// Draw the y-axis:
ctx.beginPath();
ctx.moveTo(0, CPS);
ctx.lineTo(0, -CPS);
ctx.stroke();
} // drawCoordinateAxes
</script>
</body>
</html>
```

The key functions to examine are `initializeCoordinateSystem`

and `drawMandelbrotSet`

.

### initializeCoordinateSystem

As the name implies, `initializeCoordinateSystem`

initializes the coordinate system used in the 600 pixel by 600 pixel canvas. By default, the point (0, 0) is always in the upper-left corner of the canvas. To use our familiar Cartesian coordinate system, we must **translate** this origin to the center of the canvas as follows:

```
ctx.translate(globals.canvas.width / 2, globals.canvas.height / 2);
```

We now have the origin of the coordinate system centered but the positive y-axis is pointing down (increasing) when it should be pointing up (increasing). To correct this, we can use the **scale** method as follows:

```
ctx.scale(1, -1);
```

This has the effect of flipping the *y*-axis (while leaving the *x*-axis unchanged), resulting in a traditional Cartesian coordinate system.

### drawMandelbrotSet

The **scale** method can also be used to increase or decrease the size of a rendered image (the larger the scaling factor, the larger the rendered image becomes). We use this method in the example code as follows:

```
ctx.scale(1/DELTA, -1/DELTA);
```

If `DELTA`

is small (say 0.008) and also represents the size of a canvas pixel, a normally tiny image is significantly increased in size in that `ctx.scale(1/DELTA, -1/DELTA)`

becomes `ctx.scale(125, -125)`

. This becomes apparent when we examine `drawMandelbrotSet`

:

drawMandelbrotSet

```
function drawMandelbrotSet() {
var ctx = globals.canvas.ctx;
for (var Re = -CPS; Re <= CPS; Re = Re + DELTA) { // Represents the Re-axis. Re represents the real part of a complex c value.
next_c_value: // "continue next_c_value;" is equivalent to an old school GOTO statement (which can be very handy in deeply nested loops).
for (var Im = -CPS; Im <= CPS; Im = Im + DELTA) { // Represents the Im-axis. Im represents the imaginary part of a complex c value.
var z = new Complex(0, 0); // Represents Zo (where "o" indicates subscript 0).
var c = new Complex(Re, Im); // Represents a complex c value, which either does or does not belong to the Mandelbrot set, as determined in the next FOR loop.
for (var iterationCount = 1; iterationCount <= MAX_ITERATIONS; iterationCount++) {
z = c.add( z.square() ); // Performs Zn+1 = (Zn)^2 + c
if (z.modulus() > 2) {
continue next_c_value; // The complex c value is not part of the Mandelbrot set, so immediately check the next one.
} // if
} // for
// Assert: z.modulus() <= 2, therefore the complex c value is probably a member of the Mandelbrot set - increase MAX_ITERATIONS to improve this determination.
ctx.fillRect(Re, Im, DELTA, DELTA); // This c value is probably part of the Mandelbrot set, so color this pixel black. A "pixel" for the canvas is a DELTA x DELTA black square.
} // for
} // for
} // drawMandelbrotSet
```

In that `CPS`

equals 2, we’re examining each grid point in the following square (grid points not shown):

The number of grid points within this square is determined by `DELTA`

. As `DELTA`

decreases, the number of grid points increases:

```
for (var Re = -CPS; Re <= CPS; Re = Re + DELTA) {
…
for (var Im = -CPS; Im <= CPS; Im = Im + DELTA) {
…
}
}
```

We next set *z*₀ to 0 + 0*i* = 0 as required and create *c* based on which grid point (Re, Im) we’re currently examining:

```
var z = new Complex(0, 0);
var c = new Complex(Re, Im);
```

We can now determine if *c* is (more than likely) in the Mandelbrot set by calculating |*z*ₙ| (within a reasonable number of iterations) and checking if |*z*ₙ| is greater than 2:

```
for (var iterationCount = 1; iterationCount <= MAX_ITERATIONS; iterationCount++) {
z = c.add( z.square() ); // Performs Zn+1 = (Zn)^2 + c
if (z.modulus() > 2) {
continue next_c_value; // The complex c value is not part of the Mandelbrot set, so immediately check the next one.
} // if
} // for
```

Why |*z*ₙ| > 2? Because, and as described in Basic properties, if the absolute value of *z*ₙ ever becomes larger than 2, the sequence will always escape to infinity (indicating that *c* is not part of the Mandelbrot set).

If, after `MAX_ITERATIONS`

, the absolute value of *z*ₙ is less than or equal to 2 (`z.modulus() <= 2`

), the *c* value associated with *z*ₙ is more than likely a member of the Mandelbrot set. Thus, we color *c*’s grid point black:

```
ctx.fillRect(cx, cy, DELTA, DELTA);
```

Lastly, we draw two red coordinate axes by calling `drawCoordinateAxes`

.

One of the most egregious issues with Mandelbrot 1 is that we cannot zoom in to examine the finer details of the Mandelbrot set. In order to facilitate this necessary feature, we must first determine how to map a canvas screen point (in pixels) to the complex plane, which is discussed in Mapping screen coordinates to the complex plane.