# Hokusai and fractals

Rendering fractals using HTML5 canvas

This demo is based on Tomas Petricek's F# Advent Calendar post that explores Japanese art using the (now defunct) Freebase type provider and renders The Great Wave by Hokusai using the Julia fractal. You can find the full source code on GitHub.

In this demo, you'll see how to define a simple complex number arithmetic in F#, how to use it to implement the Julia set fractal and how to render the fractal asynchronously to avoid blocking the browser during the process. To run the demo, click the "Render Julia set fractal" button!

## Complex numbers

Before looking at the fractal, we need a simple type for working with complex numbers that supports the `+` operation and the `abs` and `pow` functions. We define the type as a simple wrapper over a pair of floating point numbers and add `Abs` and `+` as static methods. This way, they can be used through the usual F# functions:

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: ``` ``````type Complex = | Complex of float * float /// Calculate the absolute value of a complex number static member Abs(Complex(r, i)) = let num1, num2 = abs r, abs i if (num1 > num2) then let num3 = num2 / num1 num1 * sqrt(1.0 + num3 * num3) elif num2 = 0.0 then num1 else let num4 = num1 / num2 num2 * sqrt(1.0 + num4 * num4) /// Add real and imaginary components pointwise static member (+) (Complex(r1, i1), Complex(r2, i2)) = Complex(r1+r2, i1+i2) ``````

Before moving forward, we also need to calculate a power of complex numbers. To do this, we define a `Pow` function in a helper module:

 ```1: 2: 3: 4: 5: 6: 7: 8: ``` ``````module ComplexModule = /// Calculates nth power of a complex number let Pow(Complex(r, i), power) = let num = Complex.Abs(Complex(r, i)) let num2 = atan2 i r let num3 = power * num2 let num4 = num ** power Complex(num4 * cos(num3), num4 * sin(num3)) ``````

## Calculating the Julia set

Now we have all we need to calculate the Julia set fractal. We choose a carefuly chosen (handcrafted!) starting point. Then we create a sequence of powers using F# sequence expressions:

 ```1: 2: 3: 4: 5: 6: 7: 8: 9: ``` ``````/// Constant that generates nice fractal let c = Complex(-0.70176, -0.3842) /// Generates sequence for given coordinates let iterate x y = let rec loop current = seq { yield current yield! loop (ComplexModule.Pow(current, 2.0) + c) } loop (Complex(x, y)) ``````

The `iterate` lazilly function generates potentially infinite sequence of values. We take at most `max` iterations or stop when the absolute value of the number is greater than 2. This can be nicely written using `Seq` functions from the standard F# library (supported by Fable):

 ```1: 2: 3: 4: 5: ``` ``````let countIterations max x y = iterate x y |> Seq.take (max - 1) |> Seq.takeWhile (fun v -> Complex.Abs(v) < 2.0) |> Seq.length ``````

## Generating the color palette

To generate a pretty picture, we need to carefuly generate the color palette. To do this, we define a pair of operators that let us write `(rgb1) --n--> (rbg2)` and generate a range of colors between `rgb1` and `rgb2` consisting of `n` steps.

 ```1: 2: 3: 4: 5: 6: 7: 8: ``` ``````// Transition between colors in 'count' steps let (--) clr count = clr, count let (-->) ((r1,g1,b1), count) (r2,g2,b2) = [ for c in 0 .. count - 1 -> let k = float c / float count let mid v1 v2 = (float v1 + ((float v2) - (float v1)) * k) (mid r1 r2, mid g1 g2, mid b1 b2) ] ``````

Now we can generate palette that is based on Hokusai's famous painting:

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: ``` ``````// Palette with colors used by Hokusai let palette = [| // 3x sky color & transition to light blue yield! (245,219,184) --3--> (245,219,184) yield! (245,219,184) --4--> (138,173,179) // to dark blue and then medium dark blue yield! (138,173,179) --4--> (2,12,74) yield! (2,12,74) --4--> (61,102,130) // to wave color, then light blue & back to wave yield! (61,102,130) -- 8--> (249,243,221) yield! (249,243,221) --32--> (138,173,179) yield! (138,173,179) --32--> (61,102,130) |] ``````

## Drawing the fractal

The last step is to render the fractal. To do that, we first define a couple of constants and helpers. The following constants define what part of the fractal we're rendering and how big is the canvas:

 ```1: 2: 3: 4: 5: 6: 7: ``` ``````// Specifies what range of the set to draw let w = -0.4, 0.4 let h = -0.95, -0.35 // Create bitmap that matches the size of the canvas let width = 400.0 let height = 300.0 ``````

Next, we define `setPixel` that sets the RGBA colours of a specified pixel in the canvas and we'll use F# dynamic operator so that `doc?canvas` returns an HTML element with ID `canvas`:

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: ``` ``````/// Set pixel value in ImageData to a given color let setPixel (img:ImageData) x y width (r, g, b) = let index = (x + y * int width) * 4 img.data.[index+0] <- r img.data.[index+1] <- g img.data.[index+2] <- b img.data.[index+3] <- 255.0 /// Dynamic operator that returns HTML element by ID let (?) (doc:Document) name :'R = doc.getElementById(name) :?> 'R ``````

The rendering itself is written as an F# asynchronous workflow. The workflow sleeps for 1ms after rendering each line of the fractal. Behind the scenes, this unblocks the window via a timer, so that the JavaScript function call does not block the browser while running.

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: ``` ``````/// Render fractal asynchronously with sleep after every line let render () = async { // Get element & create image for drawing let canv : HTMLCanvasElement = document?canvas let ctx = canv.getContext_2d() let img = ctx.createImageData(U2.Case1 (float width), float height) // For each pixel, transform to the specified range // and get color using countInterations and palette for x in 0 .. int width - 1 do for y in 0 .. int height - 1 do let x' = (float x / width * (snd w - fst w)) + fst w let y' = (float y / height * (snd h - fst h)) + fst h let it = countIterations palette.Length x' y' setPixel img x y width palette.[it] // Insert non-blocking waiting & update the fractal do! Async.Sleep(1) ctx.putImageData(img, 0.0, 0.0) } ``````

Now we just need to register the event handler for the `go` button and start the asynchronous workflow to do the rendering. Note that this is done using `Async.StartImmediate`:

 ```1: 2: 3: 4: 5: ``` ``````/// Setup button event handler to start the rendering let go : HTMLButtonElement = document?go go.addEventListener_click(fun _ -> render() |> Async.StartImmediate; null) ``````
namespace Fable
namespace Fable.Core
namespace Fable.Import
module Browser

from Fable.Import
Multiple items
union case Complex.Complex: float * float -> Complex

--------------------
type Complex =
| Complex of float * float
static member Abs : Complex -> float
static member ( + ) : Complex * Complex -> Complex

Full name: Hokusai.Complex
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
static member Complex.Abs : Complex -> float

Full name: Hokusai.Complex.Abs

Calculate the absolute value of a complex number
val r : float
val i : float
val num1 : float
val num2 : float
val abs : value:'T -> 'T (requires member Abs)

Full name: Microsoft.FSharp.Core.Operators.abs
val num3 : float
val sqrt : value:'T -> 'U (requires member Sqrt)

Full name: Microsoft.FSharp.Core.Operators.sqrt
val num4 : float
val r1 : float
val i1 : float
val r2 : float
val i2 : float
val Pow : Complex * power:float -> Complex

Full name: Hokusai.ComplexModule.Pow

Calculates nth power of a complex number
val power : float
val num : float
static member Complex.Abs : Complex -> float

Calculate the absolute value of a complex number
val atan2 : y:'T1 -> x:'T1 -> 'T2 (requires member Atan2)

Full name: Microsoft.FSharp.Core.Operators.atan2
val cos : value:'T -> 'T (requires member Cos)

Full name: Microsoft.FSharp.Core.Operators.cos
val sin : value:'T -> 'T (requires member Sin)

Full name: Microsoft.FSharp.Core.Operators.sin
val c : Complex

Full name: Hokusai.c

Constant that generates nice fractal
val iterate : x:float -> y:float -> seq<Complex>

Full name: Hokusai.iterate

Generates sequence for given coordinates
val x : float
val y : float
val loop : (Complex -> seq<Complex>)
val current : Complex
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
module ComplexModule

from Hokusai
val countIterations : max:int -> x:float -> y:float -> int

Full name: Hokusai.countIterations
val max : int
module Seq

from Microsoft.FSharp.Collections
val take : count:int -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.take
val takeWhile : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.takeWhile
val v : Complex
val length : source:seq<'T> -> int

Full name: Microsoft.FSharp.Collections.Seq.length
val clr : 'a
val count : 'b
val r1 : int
val g1 : int
val b1 : int
val count : int
val r2 : int
val g2 : int
val b2 : int
val c : int
val k : float
val mid : (int -> int -> float)
val v1 : int
val v2 : int
val palette : (float * float * float) []

Full name: Hokusai.palette
val w : float * float

Full name: Hokusai.w
val h : float * float

Full name: Hokusai.h
val width : float

Full name: Hokusai.width
val height : float

Full name: Hokusai.height
val setPixel : img:ImageData -> x:int -> y:int -> width:float -> r:float * g:float * b:float -> unit

Full name: Hokusai.setPixel

Set pixel value in ImageData to a given color
val img : ImageData
Multiple items
val ImageData : ImageDataType

Full name: Fable.Import.Browser.ImageData

--------------------
type ImageData =
interface
abstract member data : Uint8ClampedArray
abstract member height : float
abstract member width : float
abstract member data : Uint8ClampedArray with set
abstract member height : float with set
abstract member width : float with set
end

Full name: Fable.Import.Browser.ImageData
val x : int
val y : int
val width : float
val g : float
val b : float
val index : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
property ImageData.data: Fable.Import.JS.Uint8ClampedArray
val doc : Document
Multiple items
val Document : DocumentType

Full name: Fable.Import.Browser.Document

--------------------
type Document =
interface
inherit DocumentEvent
inherit NodeSelector
inherit GlobalEventHandlers
inherit Node
abstract member addEventListener : type:string * listener:EventListenerOrEventListenerObject * ?useCapture:bool -> unit
abstract member addEventListener_MSContentZoom : listener:Func<UIEvent,obj> * ?useCapture:bool -> unit
abstract member addEventListener_MSGestureChange : listener:Func<MSGestureEvent,obj> * ?useCapture:bool -> unit
abstract member addEventListener_MSGestureDoubleTap : listener:Func<MSGestureEvent,obj> * ?useCapture:bool -> unit
abstract member addEventListener_MSGestureEnd : listener:Func<MSGestureEvent,obj> * ?useCapture:bool -> unit
abstract member addEventListener_MSGestureHold : listener:Func<MSGestureEvent,obj> * ?useCapture:bool -> unit
...
end

Full name: Fable.Import.Browser.Document
val name : string
abstract member Document.getElementById : elementId:string -> HTMLElement
val render : unit -> Async<unit>

Full name: Hokusai.render

Render fractal asynchronously with sleep after every line
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val canv : HTMLCanvasElement
Multiple items
val HTMLCanvasElement : HTMLCanvasElementType

Full name: Fable.Import.Browser.HTMLCanvasElement

--------------------
type HTMLCanvasElement =
interface
inherit HTMLElement
abstract member getContext : contextId:string * [<ParamArray>] args:obj [] -> U2<CanvasRenderingContext2D,WebGLRenderingContext>
abstract member getContext_2d : unit -> CanvasRenderingContext2D
abstract member ( getContext_experimental-webgl ) : unit -> WebGLRenderingContext
abstract member height : float
abstract member width : float
abstract member msToBlob : unit -> Blob
abstract member height : float with set
abstract member width : float with set
abstract member toBlob : unit -> Blob
...
end

Full name: Fable.Import.Browser.HTMLCanvasElement
val document : Document

Full name: Fable.Import.Browser.document
val ctx : CanvasRenderingContext2D
abstract member HTMLCanvasElement.getContext_2d : unit -> CanvasRenderingContext2D
abstract member CanvasRenderingContext2D.createImageData : imageDataOrSw:U2<float,ImageData> * ?sh:float -> ImageData
type U2<'a,'b> =
| Case1 of 'a
| Case2 of 'b

Full name: Fable.Core.U2<_,_>
union case U2.Case1: 'a -> U2<'a,'b>
val x' : float
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val y' : float
val it : int
property System.Array.Length: int
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member Choice : computations:seq<Async<'T option>> -> Async<'T option>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
abstract member CanvasRenderingContext2D.putImageData : imagedata:ImageData * dx:float * dy:float * ?dirtyX:float * ?dirtyY:float * ?dirtyWidth:float * ?dirtyHeight:float -> unit
val go : HTMLButtonElement

Full name: Hokusai.go

Setup button event handler to start the rendering
Multiple items
val HTMLButtonElement : HTMLButtonElementType

Full name: Fable.Import.Browser.HTMLButtonElement

--------------------
type HTMLButtonElement =
interface
inherit HTMLElement
abstract member checkValidity : unit -> bool
abstract member createTextRange : unit -> TextRange
abstract member autofocus : bool
abstract member disabled : bool
abstract member form : HTMLFormElement
abstract member formAction : string
abstract member formEnctype : string
abstract member formMethod : string
abstract member formNoValidate : string
...
end

Full name: Fable.Import.Browser.HTMLButtonElement
abstract member HTMLElement.addEventListener_click : listener:System.Func<MouseEvent,obj> * ?useCapture:bool -> unit
static member Async.StartImmediate : computation:Async<unit> * ?cancellationToken:System.Threading.CancellationToken -> unit