``````<!-- Used to control the zoom level etc. -->
<div class="controls">
<div>
Zoom size:
<input type="range" min="2" max="50" value="10" id="zoomsize">
</div>

<input type="button" id="reset" value="Reset">
</div>

<!-- A little box that shows what part of the Mandelbrot set will be shown on click -->
<div class="selector"></div>

<!-- The canvas we'll render the Mandelbrot set on -->
<canvas class="canvas" />``````
``````html, body {
margin: 0;
height: 100%;
}
.controls {
position: fixed;
background-color: #f0f0f0;
z-index: 1000;
}
.selector {
border: 2px solid #000;
opacity: .2;
position: fixed;
z-index: 999;
transform: translate(-50%, -50%);
pointer-events: none;
}
.canvas {
width: 100%;
height: 100vh;
}``````
``````class Complex {
constructor(real, imaginary) {
this.real = real
this.imaginary = imaginary
}

plus(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
)
}

times(other) {
return new Complex(
(this.real * other.real - this.imaginary * other.imaginary),
(this.real * other.imaginary + other.real * this.imaginary)
)
}
}

/**
* Calculates n+1
*/
const iterate = (n, c) => n.times(n).plus(c)

/**
* Checks if a complex number `c` converges according to the Mandelbrot definition.
*/
const doesDiverge = (c, maxIter) => {
let n = new Complex(0, 0)
for (let i = 0; i < maxIter; i++) {
n = iterate(n, c)
}

// If the iteration diverges, these values will be NaN quite fast. Around 100 iterations is needed.
return isNaN(n.real) || isNaN(n.imaginary)
}

/**
* Draws the Mandelbrot set.
*/
const drawMandelbrotSet = (realFrom, realTo, imagFrom, imagTo) => {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')

const winWidth = window.innerWidth
const winHeight = window.innerHeight

// Reset the canvas
canvas.width = winWidth
canvas.height = winHeight
ctx.clearRect(0, 0, winWidth, winHeight)

// Determine how big a change in number a single pixel is
const stepSizeReal = (realTo - realFrom) / winWidth
const stepSizeImaginary = (imagTo - imagFrom) / winHeight

// Loop through every pixel of the complex plane that is currently visible
for (let x = realFrom; x <= realTo; x += stepSizeReal) {
for (let y = imagFrom; y <= imagTo; y += stepSizeImaginary) {
// Determine if this coordinate is part of the Mandelbrot set.
const c = new Complex(x, y)
const isInMandelbrotSet = !doesDiverge(c, 50)

const r = isInMandelbrotSet ? 67 : 104
const g = isInMandelbrotSet ? 65 : 211
const b = isInMandelbrotSet ? 144 : 145

// Cast the coordinates on the complex plane back to actual pixel coordinates
const screenX = (x - realFrom) / (realTo - realFrom) * winWidth
const screenY = (y - imagFrom) / (imagTo - imagFrom) * winHeight

// Draw a single pixel
ctx.fillStyle = `rgb(\${r}, \${g}, \${b})`
ctx.fillRect(screenX, screenY, 1, 1)
}
}
}

// X coordinate
const realInitial = {
from: -2,
to: 2,
}

// Y coordinate, keep the aspect ratio
const imagInitial = {
from: realInitial.from / window.innerWidth * window.innerHeight,
to: realInitial.to / window.innerWidth * window.innerHeight,
}

// Ranging from negative to positive - which part of the plane is visible right now?
let real = realInitial
let imag = imagInitial

real = realInitial
imag = imagInitial

drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
})

// Size of the zoom compared to current screen size
let zoomsize = 10

/**
* Makes the selector follow the mouse
*/
const selector = document.querySelector('.selector')
console.log(event.clientX, event.clientY)
selector.style.top = `\${event.clientY}px`
selector.style.left = `\${event.clientX}px`
selector.style.width = `\${window.innerWidth / zoomsize}px`
selector.style.height = `\${window.innerHeight / zoomsize}px`
})

/**
*/
zoomsize = parseInt(event.target.value)
})

/**
* Perform a zoom
*/
const winWidth = window.innerWidth
const winHeight = window.innerHeight

const selectedWidth = winWidth / zoomsize
const selectedHeight = winHeight / zoomsize

const startX =  (event.clientX - (selectedWidth / 2)) / winWidth
const endX = (event.clientX + (selectedWidth / 2)) / winWidth
const startY = (event.clientY - (selectedHeight / 2)) / winHeight
const endY = (event.clientY + (selectedHeight / 2)) / winHeight

real = {
from: ((real.to - real.from) * startX) + real.from,
to: ((real.to - real.from) * endX) + real.from,
}

imag = {
from: ((imag.to - imag.from) * startY) + imag.from,
to: ((imag.to - imag.from) * endY) + imag.from,
}

drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
})

drawMandelbrotSet(real.from, real.to, imag.from, imag.to)``````

### External CSS

This Pen doesn't use any external CSS resources.

### External JavaScript

This Pen doesn't use any external JavaScript resources.